/*
 * Copyright 2011, 2012, 2013 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Support IE and Node constant
 */
 
try {
    if (Node.ELEMENT_NODE != 1) {
        throw true;
    }
}
catch(e) {
    var Node = {
        ELEMENT_NODE:                1,
        ATTRIBUTE_NODE:              2,
        TEXT_NODE:                   3,
        CDATA_SECTION_NODE:          4,
        ENTITY_REFERENCE_NODE:       5,
        ENTITY_NODE:                 6,
        PROCESSING_INSTRUCTION_NODE: 7,
        COMMENT_NODE:                8,
        DOCUMENT_NODE:               9,
        DOCUMENT_TYPE_NODE:         10,
        DOCUMENT_FRAGMENT_NODE:     11,
        NOTATION_NODE:              12
    };
}

/* ---------------------------------------------------------------- */
/*                       OpenAjax Constants                         */ 
/* ---------------------------------------------------------------- */


/** 
 * @namespace OpenAjax
 */

var OpenAjax = OpenAjax || {};
 
/** 
 * @namespace OpenAjax.a11y
 */

OpenAjax.a11y = OpenAjax.a11y || {};
OpenAjax.a11y.VERSION = "0.9.4";

/**
 * @method getVersion
 *
 * @memberOf OpenAjax.a11y
 *
 * @desc Get the current version of the evaluation library, rules and rulesets
 *
 * @return  {String}  Returns the version number of the evaluation library
 */

OpenAjax.a11y.getVersion = function () {
  return this.VERSION;
};

/** 
 * @namespace OpenAjax.a11y.cache
 */

OpenAjax.a11y.cache = OpenAjax.a11y.cache || {};

/** 
 * @namespace OpenAjax.a11y.nls
 */

OpenAjax.a11y.nls = OpenAjax.a11y.nls || {};

/** 
 * @constant EVENT_HANDLER_PROCESSOR
 * @memberOf OpenAjax.a11y
 * @type String
 * @default 'none'
 * @desc Defines an event handler enumeration method
 *       Since there is no standard way to enumerate event handlers
 *       need to use proprietary methods to get event information.
 *
 *       Current support:
 *       'firefox'  : uses Firefox (Mozilla) component technology to get 
 *                    event information
 *       'fae-util' : uses HTMLUnit features of fae-util to find event 
 *                    information
 *       'none'     : disables gathering of event information and 
 *                    therefore event related rules are not analyzed
 */
OpenAjax.a11y.EVENT_HANDLER_PROCESSOR = "none";

/**
 * @constant URL_TESTING_ENABLED
 *
 * @memberOf OpenAjax.a11y
 *
 * @type Boolean
 * @default false
 * @desc Enable or disable testing of broken links
 *       the default should be false, due to performance issues
 *       of testing links
 */
OpenAjax.a11y.URL_TESTING_ENABLED  = false;  

/**
 * @constant SUPPORTS_URL_TESTING
 *
 * @memberOf OpenAjax.a11y
 *
 * @type Boolean
 * @default false
 * @desc If true the analysis engine supports URL testing
 */
OpenAjax.a11y.SUPPORTS_URL_TESTING = true;

/**
 * @constant DATA_TABLE_ASSUMPTION
 *
 * @memberOf OpenAjax.a11y
 *
 * @type Boolean
 * @default true
 * @desc If true assume table markup is for a data table
 *       If false assume table markup is for layout, unless header cells or other 
 *       information indciates its a data table
 */
OpenAjax.a11y.DATA_TABLE_ASSUMPTION  = true;  


/**
 * @constant ELEMENT_FORMATING
 * @memberOf OpenAjax.a11y
 * @type String
 * @default 'CAPS'
 * @desc Defines the formating of element names in NLS message strings
 */
OpenAjax.a11y.ELEMENT_FORMATING = 'CAPS';


/**
 * @constant RESULT_FILTER
 * @memberOf OpenAjax.a11y
 * @type Number
 * @desc Constants related to filtering both node results and rule results
 * @example
 * OpenAjax.a11y.RESULT_FILTER.ALL
 * OpenAjax.a11y.RESULT_FILTER.PASS
 * OpenAjax.a11y.RESULT_FILTER.VIOLATION
 * OpenAjax.a11y.RESULT_FILTER.WARNING
 * OpenAjax.a11y.RESULT_FILTER.WEBSITE_MANUAL_CHECK
 * OpenAjax.a11y.RESULT_FILTER.ELEMENT_MANUAL_CHECK
 * OpenAjax.a11y.RESULT_FILTER.HIDDEN
 * OpenAjax.a11y.RESULT_FILTER.NA
 */ 
OpenAjax.a11y.RESULT_FILTER = OpenAjax.a11y.RESULT_FILTER || {
  PASS                 : 0x0001,
  VIOLATION            : 0x0002,
  WARNING              : 0x0004,
  WEBSITE_MANUAL_CHECK : 0x0008,
  PAGE_MANUAL_CHECK    : 0x0010,
  ELEMENT_MANUAL_CHECK : 0x0020,
  MANUAL_CHECK         : 0x0038,
  HIDDEN               : 0x0040, // hidden only applies to node results 
  NOT_APPLICABLE       : 0x0080,  // not applicable only applies to rule results
  PAGE                 : 0x0100,  
  ALL                  : 0x01FF
};

/**
 * @constant DEFAULT_PREFS
 * @memberOf OpenAjax.a11y
 * @type Object
 * @desc Default setting for consumers of the OpenAjax cache 
 */

OpenAjax.a11y.DEFAULT_PREFS = OpenAjax.a11y.DEFAULT_PREFS || {
  RULESET_ID     : "WCAG20_ARIA_TRANS",
  WCAG20_LEVEL   : 3,
  BROKEN_LINKS   : false
};


/**
 * @constant WCAG20_PRINCIPLE
 * @memberOf OpenAjax.a11y
 * @type Number
 * @desc Numercial constant representing a WCAG 2.0 Principles
 *
 * @example
 * OpenAjax.a11y.WCAG20_PRINCIPLE.P_1  
 * OpenAjax.a11y.WCAG20_PRINCIPLE.P_2  
 * OpenAjax.a11y.WCAG20_PRINCIPLE.P_3  
 * OpenAjax.a11y.WCAG20_PRINCIPLE.P_4  
 */
OpenAjax.a11y.WCAG20_PRINCIPLE = OpenAjax.a11y.WCAG20_PRINCIPLE || {
  P_1          : 0x000001,
  P_2          : 0x000002,
  P_3          : 0x000004,
  P_4          : 0x000008,
  ALL          : 0x00000F
};

/**
 * @constant WCAG20_GUIDELINE
 * @memberOf OpenAjax.a11y
 * @type Number
 * @desc Numercial constant representing a WCAG 2.0 Guidelines
 *
 * @example
 * OpenAjax.a11y.WCAG20_GUIDELINE.G_1_1  
 * OpenAjax.a11y.WCAG20_GUIDELINE.G_1_2  
 * OpenAjax.a11y.WCAG20_GUIDELINE.G_1_3  
 * OpenAjax.a11y.WCAG20_GUIDELINE.G_1_4  
 * OpenAjax.a11y.WCAG20_GUIDELINE.G_2_1  
 * OpenAjax.a11y.WCAG20_GUIDELINE.G_2_2  
 * OpenAjax.a11y.WCAG20_GUIDELINE.G_2_3  
 * OpenAjax.a11y.WCAG20_GUIDELINE.G_2_4  
 * OpenAjax.a11y.WCAG20_GUIDELINE.G_3_1  
 * OpenAjax.a11y.WCAG20_GUIDELINE.G_3_2  
 * OpenAjax.a11y.WCAG20_GUIDELINE.G_3_3  
 * OpenAjax.a11y.WCAG20_GUIDELINE.G_4_1  
 */
OpenAjax.a11y.WCAG20_GUIDELINE = OpenAjax.a11y.WCAG20_GUIDELINE || {
  G_1_1          : 0x000010,
  G_1_2          : 0x000020,
  G_1_3          : 0x000040,
  G_1_4          : 0x000080,
  G_2_1          : 0x000100,
  G_2_2          : 0x000200,
  G_2_3          : 0x000400,
  G_2_4          : 0x000800,
  G_3_1          : 0x001000,
  G_3_2          : 0x002000,
  G_3_3          : 0x004000,
  G_4_1          : 0x010000,
  ALL            : 0x00FFF0
};

/**
 * @constant WCAG20_SUCCESS_CRITERION
 * @memberOf OpenAjax.a11y
 * @type Number
 * @desc Numercial constant representing a WCAG 2.0 Success Criteria
 *
 * @example
 * OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_1_1_1  
 * ....
 * OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_4_1_2  
 */
OpenAjax.a11y.WCAG20_SUCCESS_CRITERION = OpenAjax.a11y.WCAG20_SUCCESS_CRITERION || {
  SC_1_1_1          : 1101,
  SC_1_2_1          : 1201,
  SC_1_2_2          : 1202,
  SC_1_2_3          : 1203,
  SC_1_2_4          : 1204,
  SC_1_2_5          : 1205,
  SC_1_2_6          : 1206,
  SC_1_2_7          : 1207,
  SC_1_2_8          : 1208,
  SC_1_2_9          : 1209,
  SC_1_3_1          : 1301,
  SC_1_3_2          : 1302,
  SC_1_3_3          : 1303,
  SC_1_4_1          : 1401,
  SC_1_4_2          : 1402,
  SC_1_4_3          : 1403,
  SC_1_4_4          : 1404,
  SC_1_4_5          : 1405,
  SC_1_4_6          : 1406,
  SC_1_4_7          : 1407,
  SC_1_4_8          : 1408,
  SC_1_4_9          : 1409,
  SC_2_1_1          : 2101,
  SC_2_1_2          : 2102,
  SC_2_1_3          : 2103,
  SC_2_2_1          : 2201,
  SC_2_2_2          : 2202,
  SC_2_2_3          : 2203,
  SC_2_2_4          : 2204,
  SC_2_2_5          : 2205,
  SC_2_3_1          : 2301,
  SC_2_3_2          : 2302,
  SC_2_4_1          : 2401,
  SC_2_4_2          : 2402,
  SC_2_4_3          : 2403,
  SC_2_4_4          : 2404,
  SC_2_4_5          : 2405,
  SC_2_4_6          : 2406,
  SC_2_4_7          : 2407,
  SC_2_4_8          : 2408,
  SC_2_4_9          : 2409,
  SC_2_4_10         : 2410,
  SC_3_1_1          : 3101,
  SC_3_1_2          : 3102,
  SC_3_1_3          : 3103,
  SC_3_1_4          : 3104,
  SC_3_1_5          : 3105,
  SC_3_1_6          : 3106,
  SC_3_2_1          : 3201,
  SC_3_2_2          : 3202,
  SC_3_2_3          : 3203,
  SC_3_2_4          : 3204,
  SC_3_2_5          : 3205,
  SC_3_3_1          : 3301,
  SC_3_3_2          : 3302,
  SC_3_3_3          : 3303,
  SC_3_3_4          : 3304,
  SC_3_3_5          : 3305,
  SC_3_3_6          : 3306,
  SC_4_1_1          : 4101,
  SC_4_1_2          : 4102
};

/**
 * @constant RULE_CATEGORIES
 * @memberOf OpenAjax.a11y
 * @type Number
 * @desc Numercial constant representing a rule category and is bit maskable
 *
 * @example
 * OpenAjax.a11y.RULE_CATEGORIES.UNDEFINED  
 * OpenAjax.a11y.RULE_CATEGORIES.AUDIO_VIDEO      
 * OpenAjax.a11y.RULE_CATEGORIES.DATA_TABLES
 * OpenAjax.a11y.RULE_CATEGORIES.FORM_CONTROLS      
 * OpenAjax.a11y.RULE_CATEGORIES.IMAGES      
 * OpenAjax.a11y.RULE_CATEGORIES.KEYBOARD_SUPPORT
 * OpenAjax.a11y.RULE_CATEGORIES.LINKS      
 * OpenAjax.a11y.RULE_CATEGORIES.NAVIGATION_FINDABILITY
 * OpenAjax.a11y.RULE_CATEGORIES.STRUCTURE_CONTENT
 * OpenAjax.a11y.RULE_CATEGORIES.STYLES_READABILITY
 * OpenAjax.a11y.RULE_CATEGORIES.WIDGETS_SCRIPTS
 */

OpenAjax.a11y.RULE_CATEGORIES = OpenAjax.a11y.RULE_CATEGORIES || {
  UNDEFINED              : 0x0000, 
  AUDIO_VIDEO            : 0x0001,  
  DATA_TABLES            : 0x0002,
  FORM_CONTROLS          : 0x0004,
  IMAGES                 : 0x0008,
  KEYBOARD_SUPPORT       : 0x0010,
  LINKS                  : 0x0020,
  NAVIGATION_FINDABILITY : 0x0040,
  STRUCTURE_CONTENT      : 0x0080,
  STYLES_READABILITY     : 0x0100,
  WIDGETS_SCRIPTS        : 0x0200,
  // Composite categories
  ALL                    : 0x03FF 
};

/**
 * @constant RULE_SUMMARY
 * @memberOf OpenAjax.a11y
 * @type Number
 * @desc Numercial constant representing a rule summary option
 *
 * @example
 * OpenAjax.a11y.RULE_SUMMARY.UNDEFINED  
 * OpenAjax.a11y.RULE_SUMMARY.CATEGORIES  
 * OpenAjax.a11y.RULE_SUMMARY.WCAG20  
 * OpenAjax.a11y.RULE_SUMMARY.GUIDELINE  
 * OpenAjax.a11y.RULE_SUMMARY.RULESET_TYPE
 */
OpenAjax.a11y.RULE_SUMMARY = OpenAjax.a11y.RULE_SUMMARY || {
  UNDEFINED        : 0,
  CATEGORIES       : 1,
  WCAG20           : 10,
  PRINCIPLES       : 11,
  GUIDELINES       : 12,
  SUCCESS_CRITERIA : 13,
  RULESET_TYPE     : 20
};

/**
 * @constant ELEMENT_TYPE
 * @memberOf OpenAjax.a11y
 * @type Number
 * @desc Numercial constant representing a element type option
 *
 * @example
 * OpenAjax.a11y.ELEMENT_TYPE.UNKNOWN  
 * OpenAjax.a11y.ELEMENT_TYPE.ALL   
 * OpenAjax.a11y.ELEMENT_TYPE.AUDIO   
 * OpenAjax.a11y.ELEMENT_TYPE.AUDIO_VIDEO      
 * OpenAjax.a11y.ELEMENT_TYPE.FORM_CONTROLS      
 * OpenAjax.a11y.ELEMENT_TYPE.HEADINGS      
 * OpenAjax.a11y.ELEMENT_TYPE.HEADINGS_LANDMARKS      
 * OpenAjax.a11y.ELEMENT_TYPE.IMAGES      
 * OpenAjax.a11y.ELEMENT_TYPE.LANDMARKS      
 * OpenAjax.a11y.ELEMENT_TYPE.LANGUAGE      
 * OpenAjax.a11y.ELEMENT_TYPE.LINKS      
 * OpenAjax.a11y.ELEMENT_TYPE.LISTS      
 * OpenAjax.a11y.ELEMENT_TYPE.TABLES      
 * OpenAjax.a11y.ELEMENT_TYPE.TEXT      
 * OpenAjax.a11y.ELEMENT_TYPE.TIMING      
 * OpenAjax.a11y.ELEMENT_TYPE.VIDEO      
 * OpenAjax.a11y.ELEMENT_TYPE.WIDGETS      
 */
OpenAjax.a11y.ELEMENT_TYPE = OpenAjax.a11y.ELEMENT_TYPE || {
  UNDEFINED          : 0,
  ALL                : 1,
  ABBREVIATIONS      : 2,
  AUDIO              : 3,
  AUDIO_VIDEO        : 4,
  FORM_CONTROLS      : 5,
  HEADINGS           : 6,
  IMAGES             : 7,
  LANGUAGE           : 8,
  LANDMARKS          : 9,
  LAYOUT             : 10,
  LINKS              : 11,
  LISTS              : 12,
  TABLES             : 13,
  TEXT               : 14,
  TIMING             : 15,
  VIDEO              : 16,
  WIDGETS            : 17,
  HEADINGS_LANDMARKS : 100,
  LAYOUT_TABLES      : 101
};


/**
 * @constant CACHE_NAMES
 * @memberOf OpenAjax.a11y
 * @type Array
 * @desc Property names of specialized caches 
 */

OpenAjax.a11y.CACHE_NAMES = ['abbreviations_cache', 
                             'color_contrast_cache', 
                             'controls_cache',
                             'dom_cache',
                             'headings_landmarks_cache',
                             'images_cache',
                             'keyboard_focus_cache',
                             'languages_cache',
                             'links_cache',
                             'lists_cache',
                             'media_cache',
                             'tables_cache',
                             'text_cache'];
  
/**
 * @constant PROGRESS
 * @memberOf OpenAjax.a11y
 * @type Number
 * @desc Progress log constants
 *
 * @example
 * OpenAjax.a11y.PROGRESS.UNDEFINED  
 * OpenAjax.a11y.PROGRESS.START      
 * OpenAjax.a11y.PROGRESS.CACHE_START
 * OpenAjax.a11y.PROGRESS.CACHE_END  
 * OpenAjax.a11y.PROGRESS.REQUIREMENT    
 * OpenAjax.a11y.PROGRESS.RULE          
 * OpenAjax.a11y.PROGRESS.COMPLETE   
 */
OpenAjax.a11y.PROGRESS = OpenAjax.a11y.PROGRESS || {
  UNDEFINED   : 0,
  START       : 1,
  CACHE_START : 2,
  CACHE_END   : 3,
  REQUIREMENT : 4,   
  RULE        : 5,   
  COMPLETE    : 6   
};

/**
 * @constant WCAG20_LEVEL
 * @memberOf OpenAjax.a11y
 * @type Number
 * @desc Constants related to the level of importance of a success criteria 
 * @example
 * OpenAjax.a11y.WCAG20_LEVEL.A
 * OpenAjax.a11y.WCAG20_LEVEL.AA
 * OpenAjax.a11y.WCAG20_LEVEL.AAA
 */ 
OpenAjax.a11y.WCAG20_LEVEL = OpenAjax.a11y.WCAG20_LEVEL || {
  A       : 4,
  AA      : 2,
  AAA     : 1,
  UNKNOWN : 0
};

/**
 * @constant EVALUATION_LEVELS
 * @memberOf OpenAjax.a11y
 * @type Number
 * @desc Constants related to the level of rules that wil be evaluated  
 *       These are a bit mask for WCAG20_LEVEL constants
 * @example
 * OpenAjax.a11y.EVALUATION_LEVELS.A
 * OpenAjax.a11y.EVALUATION_LEVELS.A_AA
 * OpenAjax.a11y.EVALUATION_LEVELS.A_AA_AAA
 */ 

OpenAjax.a11y.EVALUATION_LEVELS = OpenAjax.a11y.EVALUATION_LEVELS || {
  A        : 4,
  A_AA     : 6,
  A_AA_AAA : 7
};



/**
 * @constant RULE_GROUP
 * @memberOf OpenAjax.a11y
 * @type Number
 * @desc Defines a grouping of rules 
 *
 * @example
 * OpenAjax.a11y.RULE_GROUP.RULE_CATEGORIES               
 * OpenAjax.a11y.RULE_GROUP.WCAG20            
 * OpenAjax.a11y.RULE_GROUP.ALL_RULE_LIST           
 */
OpenAjax.a11y.RULE_GROUP = OpenAjax.a11y.RULE_GROUP || {
  RULE_CATEGORIES : 1,
  WCAG20          : 2,
  ALL_RULE_LIST   : 3
};


/**
 * @constant RULE_SCOPE
 * @memberOf OpenAjax.a11y
 * @type Number
 * @desc Defines scope of a rule
 *
 * @example
 * OpenAjax.a11y.RULE_SCOPE.UNKNOWN               
 * OpenAjax.a11y.RULE_SCOPE.ELEMENT               
 * OpenAjax.a11y.RULE_SCOPE.PAGE               
 * OpenAjax.a11y.RULE_SCOPE.WEBSITE               
 */
OpenAjax.a11y.RULE_SCOPE = OpenAjax.a11y.RULE_SCOPE || {
  UNKNOWN : 0,
  ELEMENT : 1,
  PAGE    : 2,
  WEBSITE : 3
};


/**
 * @constant TEST_RESULT
 * @memberOf OpenAjax.a11y
 * @type Number
 * @desc Types of rule results, used in validation functions  
 *
 * @example
 * OpenAjax.a11y.TEST_RESULT.FAIL
 * OpenAjax.a11y.TEST_RESULT.HIDDEN
 * OpenAjax.a11y.TEST_RESULT.MANUAL_CHECK
 * OpenAjax.a11y.TEST_RESULT.NONE
 * OpenAjax.a11y.TEST_RESULT.PASS
 */
OpenAjax.a11y.TEST_RESULT = OpenAjax.a11y.TEST_RESULT || {
  PASS         : 1,
  FAIL         : 2,
  MANUAL_CHECK : 3,
  HIDDEN       : 4,
  NONE         : 5
};

/**
 * @constant RESULT_VALUE
 * @memberOf OpenAjax.a11y
 * @type Number
 * @desc Types of rule results for required and recommended rules
 *       in a ruleset
 *
 * @example
 * OpenAjax.a11y.RESULT_VALUE.HIDDEN           
 * OpenAjax.a11y.RESULT_VALUE.MANUAL_CHECK
 * OpenAjax.a11y.RESULT_VALUE.NONE    
 * OpenAjax.a11y.RESULT_VALUE.NOT_EVALUATED    
 * OpenAjax.a11y.RESULT_VALUE.PASS             
 * OpenAjax.a11y.RESULT_VALUE.VIOLATION        
 * OpenAjax.a11y.RESULT_VALUE.WARNING          
 */
OpenAjax.a11y.RESULT_VALUE = OpenAjax.a11y.RESULT_VALUE || {
  NONE           : 0,
  NOT_APPLICABLE : 1,
  PASS           : 2,
  HIDDEN         : 3,  // Content is hidden and not tested for accessibility
  MANUAL_CHECK   : 4,
  WARNING        : 5,
  VIOLATION      : 6
};

/** 
 * @constant STATUS
 * @memberOf OpenAjax.a11y
 * @type Number
 * @desc Status of rule acceptance for inclusion in a ruleset
 *
 * @example
 * OpenAjax.a11y.STATUS.UNDEFINED
 * OpenAjax.a11y.STATUS.PROPOSED 
 * OpenAjax.a11y.STATUS.ACCEPTED
 * OpenAjax.a11y.STATUS.DEPRICATED
 */
OpenAjax.a11y.STATUS = OpenAjax.a11y.STATUS || {
  UNDEFINED  : 0,
  PROPOSED   : 1,
  ACCEPTED   : 2,
  DEPRICATED : 3
};

/**
 * @constant REFERENCES
 * @memberOf OpenAjax.a11y
 * @type Number
 * @desc Types of reference for supplemential materials to help people understand an accessibility requirement and
 *       how to improve the accessibility
 *
 * @example
 * OpenAjax.a11y.REFERENCES.UNKNOWN          
 * OpenAjax.a11y.REFERENCES.SPECIFICATION     
 * OpenAjax.a11y.REFERENCES.WCAG_TECHNIQUE        
 * OpenAjax.a11y.REFERENCES.TECHNIQUE        
 * OpenAjax.a11y.REFERENCES.EXAMPLE 
 * OpenAjax.a11y.REFERENCES.MANUAL_CHECK
 * OpenAjax.a11y.REFERENCES.AUTHORING_TOOL     
 * OpenAjax.a11y.REFERENCES.OTHER         
 */
 
OpenAjax.a11y.REFERENCES = OpenAjax.a11y.REFERENCES || {
  UNKNOWN         : 0,
  AUTHORING_TOOL  : 1,
  EXAMPLE         : 2,
  LIBRARY_PRODUCT : 3,
  MANUAL_CHECK    : 4,
  OTHER           : 5,
  PURPOSE         : 6,
  RULE_CATEGORY   : 7,
  REFERENCE       : 8,
  SPECIFICATION   : 9,
  TECHNIQUE       : 10,
  WCAG_TECHNIQUE  : 11
};

/**
 * @constant VISIBILITY
 * @memberOf OpenAjax.a11y
 * @type Number
 * @desc Visbility of an item in graphical renderings and to asssitive technologies
 *
 * @example
 * OpenAjax.a11y.VISIBILITY.UNKNOWN
 * OpenAjax.a11y.VISIBILITY.HIDDEN 
 * OpenAjax.a11y.VISIBILITY.VISIBLE
 */
OpenAjax.a11y.VISIBILITY = OpenAjax.a11y.VISIBILITY || {
  UNKNOWN : 1,
  HIDDEN  : 2,
  VISIBLE : 3
};

/**
 * @constant ID
 * @memberOf OpenAjax.a11y
 * @type Number
 * @desc ID of an item 
 * @example
 * OpenAjax.a11y.ID.NOT_DEFINED
 * OpenAjax.a11y.ID.UNIQUE
 * OpenAjax.a11y.ID.NOT_UNIQUE
 */
OpenAjax.a11y.ID = OpenAjax.a11y.ID || {
  NOT_DEFINED  : 1,
  UNIQUE       : 2,
  NOT_UNIQUE   : 3
};


/**
 * @constant LIST
 * @memberOf OpenAjax.a11y
 * @type Number
 * @desc Constants related to the lists cache 
 * @example
 * OpenAjax.a11y.LIST.CONTAINER
 * OpenAjax.a11y.LIST.ITEM
 * OpenAjax.a11y.LIST.LANDMARK
 */ 
OpenAjax.a11y.LIST = OpenAjax.a11y.LIST || {
  UNDEFINED : 0,
  CONTAINER : 1,
  ITEM      : 2,
  LANDMARK  : 3
};

/**
 * @constant MEDIA
 * @memberOf OpenAjax.a11y
 * @type Number
 * @desc Constants related to the probability of being media object with audio and video 
 * @example
 * OpenAjax.a11y.MEDIA.UNDEFINED
 * OpenAjax.a11y.MEDIA.NO
 * OpenAjax.a11y.MEDIA.MAYBE
 * OpenAjax.a11y.MEDIA.YES 
 */ 
OpenAjax.a11y.MEDIA = OpenAjax.a11y.MEDIA || {
  UNDEFINED : 0,
  NO        : 1,
  MAYBE     : 2,
  YES       : 3
};

/**
 * @constant URL_RESULT
 * @memberOf OpenAjax.a11y
 * @type Number
 * @desc Staus of rule acceptance for inclusion in the public ruleset
 * @example
 * OpenAjax.a11y.URL_RESULT.INVALID
 * OpenAjax.a11y.URL_RESULT.VALID
 * OpenAjax.a11y.URL_RESULT.NOT_TESTED
 * OpenAjax.a11y.URL_RESULT.ERROR 
 */
OpenAjax.a11y.URL_RESULT = OpenAjax.a11y.URL_RESULT || {
  INVALID    :  1,
  VALID      :  2,
  NOT_TESTED :  3,
  ERROR      :  4
};

/**
 * @constant SOURCE
 * @memberOf OpenAjax.a11y
 * @type Number
 * @desc What markup was used as the source for calculating the accessible name  
 * @example
 * OpenAjax.a11y.SOURCE.NONE
 * OpenAjax.a11y.SOURCE.LABEL_REFERENCE
 * OpenAjax.a11y.SOURCE.LABEL_ENCAPSULATION
 * OpenAjax.a11y.SOURCE.TITLE_ATTRIBUTE
 * OpenAjax.a11y.SOURCE.VALUE_ATTRIBUTE
 * OpenAjax.a11y.SOURCE.ALT_ATTRIBUTE
 * OpenAjax.a11y.SOURCE.BUTTON_TYPE
 * OpenAjax.a11y.SOURCE.TEXT_CONTENT
 * OpenAjax.a11y.SOURCE.ARIA_LABELLEDBY
 * OpenAjax.a11y.SOURCE.ARIA_LABEL        
 */
OpenAjax.a11y.SOURCE = OpenAjax.a11y.SOURCE || {
  NONE                 : 1,
  LABEL_REFERENCE      : 2,
  LABEL_ENCAPSULATION  : 3,
  TITLE_ATTRIBUTE      : 4,
  VALUE_ATTRIBUTE      : 5,
  ALT_ATTRIBUTE        : 6,
  BUTTON_TYPE          : 7,
  TEXT_CONTENT         : 8,
  ARIA_LABELLEDBY      : 9,
  ARIA_LABEL           : 10,
  TABLE_CAPTION        : 11,
  TABLE_SUMMARY        : 12
};

/**
 * @constant HEADER_SOURCE
 * @memberOf OpenAjax.a11y
 * @type Number
 * @desc What markup was used as the source for table cell headers
 * @example
 * OpenAjax.a11y.HEADER_SOURCE.NONE
 * OpenAjax.a11y.HEADER_SOURCE.HEADERS_ATTRIBUTE
 * OpenAjax.a11y.HEADER_SOURCE.ROW_OR_COLUMN_HEADERS
 */
OpenAjax.a11y.HEADER_SOURCE = OpenAjax.a11y.HEADER_SOURCE || {
  NONE                  : 1,
  HEADERS_ATTRIBUTE     : 2,
  ROW_OR_COLUMN_HEADERS : 3
};


/**
 * @constant CONTROL_TYPE
 * @memberOf OpenAjax.a11y
 * @type Number
 * @desc Indentify the cache control element type 
 *
 * @example
 * OpenAjax.a11y.CONTROL_TYPE.UNKNOWN
 * OpenAjax.a11y.CONTROL_TYPE.BUTTON_ELEMENT
 * OpenAjax.a11y.CONTROL_TYPE.BUTTON_INPUT
 * OpenAjax.a11y.CONTROL_TYPE.CHECKBOX
 * OpenAjax.a11y.CONTROL_TYPE.COLOR
 * OpenAjax.a11y.CONTROL_TYPE.DATE
 * OpenAjax.a11y.CONTROL_TYPE.DATETIME
 * OpenAjax.a11y.CONTROL_TYPE.DATETIME_LOCAL
 * OpenAjax.a11y.CONTROL_TYPE.EMAIL
 * OpenAjax.a11y.CONTROL_TYPE.FIELDSET
 * OpenAjax.a11y.CONTROL_TYPE.FILE
 * OpenAjax.a11y.CONTROL_TYPE.FORM
 * OpenAjax.a11y.CONTROL_TYPE.HIDDEN
 * OpenAjax.a11y.CONTROL_TYPE.IMAGE
 * OpenAjax.a11y.CONTROL_TYPE.LABEL
 * OpenAjax.a11y.CONTROL_TYPE.METER
 * OpenAjax.a11y.CONTROL_TYPE.MONTH
 * OpenAjax.a11y.CONTROL_TYPE.NUMBER
 * OpenAjax.a11y.CONTROL_TYPE.OPTION
 * OpenAjax.a11y.CONTROL_TYPE.OPTGROUP
 * OpenAjax.a11y.CONTROL_TYPE.PASSWORD
 * OpenAjax.a11y.CONTROL_TYPE.PROGRESS
 * OpenAjax.a11y.CONTROL_TYPE.RADIO
 * OpenAjax.a11y.CONTROL_TYPE.RANGE
 * OpenAjax.a11y.CONTROL_TYPE.RESET
 * OpenAjax.a11y.CONTROL_TYPE.SEARCH
 * OpenAjax.a11y.CONTROL_TYPE.SELECT
 * OpenAjax.a11y.CONTROL_TYPE.SUBMIT
 * OpenAjax.a11y.CONTROL_TYPE.TEL
 * OpenAjax.a11y.CONTROL_TYPE.TEXT
 * OpenAjax.a11y.CONTROL_TYPE.TEXTAREA
 * OpenAjax.a11y.CONTROL_TYPE.TIME
 * OpenAjax.a11y.CONTROL_TYPE.URL
 * OpenAjax.a11y.CONTROL_TYPE.WEEK
 * OpenAjax.a11y.CONTROL_TYPE.WIDGET
 */
OpenAjax.a11y.CONTROL_TYPE = OpenAjax.a11y.CONTROL_TYPE || {
  UNKNOWN        : 1,
  BUTTON_ELEMENT : 2,
  BUTTON_INPUT   : 3,
  CHECKBOX       : 4,
  COLOR          : 5,
  DATE           : 6,
  DATETIME       : 7,
  DATETIME_LOCAL : 8,
  EMAIL          : 9,
  FIELDSET       : 10,
  FILE           : 11,
  FORM           : 12,
  HIDDEN         : 13,
  IMAGE          : 14,
  LABEL          : 15,
  METER          : 16,
  MONTH          : 17,
  NUMBER         : 18,
  OPTION         : 19,
  OPTGROUP       : 20,
  PASSWORD       : 21,
  PROGRESS       : 22,
  RADIO          : 23,
  RANGE          : 24,
  RESET          : 25,
  SEARCH         : 26,
  SELECT         : 27,
  SUBMIT         : 28,
  TEL            : 29,
  TEXT           : 30,
  TEXTAREA       : 31,
  TIME           : 32,
  URL            : 33,
  WEEK           : 34,
  WIDGET         : 35
};

/**
 * @constant MAIN
 * @memberOf OpenAjax.a11y
 * @type Number
 * @desc Constants for MAIN cache elements  
 * @example
 * OpenAjax.a11y.MAIN.ROLE_MAIN
 * OpenAjax.a11y.MAIN.H1_ELEMENT
 * OpenAjax.a11y.MAIN.TITLE_ELEMENT   
 */
OpenAjax.a11y.MAIN = OpenAjax.a11y.MAIN || {
  ROLE_MAIN      :  1,
  H1_ELEMENT     :  2,
  TITLE_ELEMENT  :  3
};

/**
 * @constant TABLE_ROLE
 * @memberOf OpenAjax.a11y
 * @type Number
 * @desc Constants for TABLE_ROLE table cache elements
 * @example
 * OpenAjax.a11y.TABLE_ROLE.UNKNOWN  
 * OpenAjax.a11y.TABLE_ROLE.LAYOUT 
 * OpenAjax.a11y.TABLE_ROLE.DATA   
 */
 
OpenAjax.a11y.TABLE_ROLE = OpenAjax.a11y.TABLE_ROLE || {
  UNKNOWN :  1,
  LAYOUT  :  2,
  DATA    :  3
};

/**
 * @constant TABLE
 * @memberOf OpenAjax.a11y
 * @type Number
 * @desc Constants for TABLE cache elements
 * @example
 * OpenAjax.a11y.TABLE.TABLE_ELEMENT   
 * OpenAjax.a11y.TABLE.CAPTION_ELEMENT 
 * OpenAjax.a11y.TABLE.THEAD_ELEMENT   
 * OpenAjax.a11y.TABLE.TBODY_ELEMENT   
 * OpenAjax.a11y.TABLE.TR_ELEMENT      
 * OpenAjax.a11y.TABLE.TH_ELEMENT      
 * OpenAjax.a11y.TABLE.TD_ELEMENT      
 */
OpenAjax.a11y.TABLE = OpenAjax.a11y.TABLE || {
  TABLE_ELEMENT   :  1,
  CAPTION_ELEMENT :  2,
  THEAD_ELEMENT   :  3,
  TBODY_ELEMENT   :  4,
  TR_ELEMENT      :  5,
  TH_ELEMENT      :  6,
  TD_ELEMENT      :  7
};

/**
 * @constant LINK_TYPE
 * @memberOf OpenAjax.a11y
 * @type Number
 * @desc Constants for LINK cache elements
 * @example
 * OpenAjax.a11y.LINK_TYPE.EMPTY
 * OpenAjax.a11y.LINK_TYPE.OTHER
 * OpenAjax.a11y.LINK_TYPE.INTERNAL
 * OpenAjax.a11y.LINK_TYPE.HTTP
 * OpenAjax.a11y.LINK_TYPE.HTTPS
 * OpenAjax.a11y.LINK_TYPE.FTP
 * OpenAjax.a11y.LINK_TYPE.FTS
 * OpenAjax.a11y.LINK_TYPE.FILE
 * OpenAjax.a11y.LINK_TYPE.JAVASCRIPT
 * OpenAjax.a11y.LINK_TYPE.MAILTO
 * OpenAjax.a11y.LINK_TYPE.TARGET
 */
OpenAjax.a11y.LINK_TYPE = OpenAjax.a11y.LINK_TYPE || {
  EMPTY      : 0,
  OTHER      : 1,
  INTERNAL   : 2,
  HTTP       : 3,
  HTTPS      : 4,
  FTP        : 5,
  FTPS       : 6,
  FILE       : 7,
  JAVASCRIPT : 8,
  MAILTO     : 9,
  TARGET     : 10
};

/**
 * @constant FILTERED_RULE_RESULT_RETURN_VALUE
 * @memberOf OpenAjax.a11y
 * @type Number
 * @desc Used for rule aggregation
 * @example
 * OpenAjax.a11y.FILTERED_RULE_RESULT_RETURN_VALUE.NO_MATCH
 * OpenAjax.a11y.FILTERED_RULE_RESULT_RETURN_VALUE.ADDED
 * OpenAjax.a11y.FILTERED_RULE_RESULT_RETURN_VALUE.NOT_ADDED
 */
OpenAjax.a11y.FILTERED_RULE_RESULT_RETURN_VALUE = OpenAjax.a11y.FILTERED_RULE_RESULT_RETURN_VALUE || {
  NO_MATCH  : 0,
  ADDED     : 1,
  NOT_ADDED : 2
};OpenAjax.a11y.LANGUAGE_CODES = OpenAjax.a11y.LANGUAGE_CODES || {
      subtags : "aa ab ae af ak am an ar as av ay az ba be bg bh bi bm bn bo br bs ca ce ch co cr cs cu cv cy da de dv dz ee el en eo es et eu fa ff fi fj fo fr fy ga gd gl gn gu gv ha he hi ho hr ht hu hy hz ia id ie ig ii ik in io is it iu iw ja ji jv jw ka kg ki kj kk kl km kn ko kr ks ku kv kw ky la lb lg li ln lo lt lu lv mg mh mi mk ml mn mo mr ms mt my na nb nd ne ng nl nn no nr nv ny oc oj om or os pa pi pl ps pt qu rm rn ro ru rw sa sc sd se sg sh si sk sl sm sn so sq sr ss st su sv sw ta te tg th ti tk tl tn to tr ts tt tw ty ug uk ur uz ve vi vo wa wo xh yi yo za zh zu aaa aab aac aad aae aaf aag aah aai aak aal aam aan aao aap aaq aas aat aau aav aaw aax aaz aba abb abc abd abe abf abg abh abi abj abl abm abn abo abp abq abr abs abt abu abv abw abx aby abz aca acb acd ace acf ach aci ack acl acm acn acp acq acr acs act acu acv acw acx acy acz ada adb add ade adf adg adh adi adj adl adn ado adp adq adr ads adt adu adw adx ady adz aea aeb aec aed aee aek ael aem aen aeq aer aes aeu aew aey aez afa afb afd afe afg afh afi afk afn afo afp afs aft afu afz aga agb agc agd age agf agg agh agi agj agk agl agm agn ago agp agq agr ags agt agu agv agw agx agy agz aha ahb ahg ahh ahi ahk ahl ahm ahn aho ahp ahr ahs aht aia aib aic aid aie aif aig aih aii aij aik ail aim ain aio aip aiq air ais ait aiw aix aiy aja ajg aji ajn ajp ajt aju ajw ajz akb akc akd ake akf akg akh aki akj akk akl akm ako akp akq akr aks akt aku akv akw akx aky akz ala alc ald ale alf alg alh ali alj alk all alm aln alo alp alq alr als alt alu alv alw alx aly alz ama amb amc ame amf amg ami amj amk aml amm amn amo amp amq amr ams amt amu amv amw amx amy amz ana anb anc and ane anf ang anh ani anj ank anl anm ann ano anp anq anr ans ant anu anv anw anx any anz aoa aob aoc aod aoe aof aog aoh aoi aoj aok aol aom aon aor aos aot aou aox aoz apa apb apc apd ape apf apg aph api apj apk apl apm apn apo app apq apr aps apt apu apv apw apx apy apz aqa aqc aqd aqg aql aqm aqn aqp aqr aqz arb arc ard are arh ari arj ark arl arn aro arp arq arr ars art aru arv arw arx ary arz asa asb asc asd ase asf asg ash asi asj ask asl asn aso asp asq asr ass ast asu asv asw asx asy asz ata atb atc atd ate atg ath ati atj atk atl atm atn ato atp atq atr ats att atu atv atw atx aty atz aua aub auc aud aue auf aug auh aui auj auk aul aum aun auo aup auq aur aus aut auu auw aux auy auz avb avd avi avk avl avm avn avo avs avt avu avv awa awb awc awd awe awg awh awi awk awm awn awo awr aws awt awu awv aww awx awy axb axe axg axk axl axm axx aya ayb ayc ayd aye ayg ayh ayi ayk ayl ayn ayo ayp ayq ayr ays ayt ayu ayx ayy ayz aza azb azc azd azg azj azm azn azo azt azz baa bab bac bad bae baf bag bah bai baj bal ban bao bap bar bas bat bau bav baw bax bay baz bba bbb bbc bbd bbe bbf bbg bbh bbi bbj bbk bbl bbm bbn bbo bbp bbq bbr bbs bbt bbu bbv bbw bbx bby bbz bca bcb bcc bcd bce bcf bcg bch bci bcj bck bcl bcm bcn bco bcp bcq bcr bcs bct bcu bcv bcw bcy bcz bda bdb bdc bdd bde bdf bdg bdh bdi bdj bdk bdl bdm bdn bdo bdp bdq bdr bds bdt bdu bdv bdw bdx bdy bdz bea beb bec bed bee bef beg beh bei bej bek bem beo bep beq ber bes bet beu bev bew bex bey bez bfa bfb bfc bfd bfe bff bfg bfh bfi bfj bfk bfl bfm bfn bfo bfp bfq bfr bfs bft bfu bfw bfx bfy bfz bga bgb bgc bgd bge bgf bgg bgi bgj bgk bgl bgm bgn bgo bgp bgq bgr bgs bgt bgu bgv bgw bgx bgy bgz bha bhb bhc bhd bhe bhf bhg bhh bhi bhj bhk bhl bhm bhn bho bhp bhq bhr bhs bht bhu bhv bhw bhx bhy bhz bia bib bic bid bie bif big bij bik bil bim bin bio bip biq bir bit biu biv biw bix biy biz bja bjb bjc bjd bje bjf bjg bjh bji bjj bjk bjl bjm bjn bjo bjp bjq bjr bjs bjt bju bjv bjw bjx bjy bjz bka bkb bkc bkd bkf bkg bkh bki bkj bkk bkl bkm bkn bko bkp bkq bkr bks bkt bku bkv bkw bkx bky bkz bla blb blc bld ble blf blg blh bli blj blk bll blm bln blo blp blq blr bls blt blv blw blx bly blz bma bmb bmc bmd bme bmf bmg bmh bmi bmj bmk bml bmm bmn bmo bmp bmq bmr bms bmt bmu bmv bmw bmx bmy bmz bna bnb bnc bnd bne bnf bng bni bnj bnk bnl bnm bnn bno bnp bnq bnr bns bnt bnu bnv bnw bnx bny bnz boa bob boe bof bog boh boi boj bok bol bom bon boo bop boq bor bot bou bov bow box boy boz bpa bpb bpd bpg bph bpi bpj bpk bpl bpm bpn bpo bpp bpq bpr bps bpt bpu bpv bpw bpx bpy bpz bqa bqb bqc bqd bqf bqg bqh bqi bqj bqk bql bqm bqn bqo bqp bqq bqr bqs bqt bqu bqv bqw bqx bqy bqz bra brb brc brd brf brg brh bri brj brk brl brm brn bro brp brq brr brs brt bru brv brw brx bry brz bsa bsb bsc bse bsf bsg bsh bsi bsj bsk bsl bsm bsn bso bsp bsq bsr bss bst bsu bsv bsw bsx bsy bta btb btc btd bte btf btg bth bti btj btk btl btm btn bto btp btq btr bts btt btu btv btw btx bty btz bua bub buc bud bue buf bug buh bui buj buk bum bun buo bup buq bus but buu buv buw bux buy buz bva bvb bvc bvd bve bvf bvg bvh bvi bvj bvk bvl bvm bvn bvo bvp bvq bvr bvt bvu bvv bvw bvx bvy bvz bwa bwb bwc bwd bwe bwf bwg bwh bwi bwj bwk bwl bwm bwn bwo bwp bwq bwr bws bwt bwu bww bwx bwy bwz bxa bxb bxc bxd bxe bxf bxg bxh bxi bxj bxk bxl bxm bxn bxo bxp bxq bxr bxs bxu bxv bxw bxx bxz bya byb byc byd bye byf byg byh byi byj byk byl bym byn byo byp byq byr bys byt byv byw byx byy byz bza bzb bzc bzd bze bzf bzg bzh bzi bzj bzk bzl bzm bzn bzo bzp bzq bzr bzs bzt bzu bzv bzw bzx bzy bzz caa cab cac cad cae caf cag cah cai caj cak cal cam can cao cap caq car cas cau cav caw cax cay caz cba cbb cbc cbd cbe cbg cbh cbi cbj cbk cbl cbn cbo cbr cbs cbt cbu cbv cbw cby cca ccc ccd cce ccg cch ccj ccl ccm ccn cco ccp ccq ccr ccs cda cdc cdd cde cdf cdg cdh cdi cdj cdm cdn cdo cdr cds cdy cdz cea ceb ceg cek cel cen cet cfa cfd cfg cfm cga cgc cgg cgk chb chc chd chf chg chh chj chk chl chm chn cho chp chq chr cht chw chx chy chz cia cib cic cid cie cih cik cim cin cip cir ciw ciy cja cje cjh cji cjk cjm cjn cjo cjp cjr cjs cjv cjy cka ckb ckh ckl ckn cko ckq ckr cks ckt cku ckv ckx cky ckz cla clc cld cle clh cli clj clk cll clm clo clt clu clw cly cma cmc cme cmg cmi cmk cml cmm cmn cmo cmr cms cmt cna cnb cnc cng cnh cni cnk cnl cno cns cnt cnu cnw cnx coa cob coc cod coe cof cog coh coj cok col com con coo cop coq cot cou cov cow cox coy coz cpa cpb cpc cpe cpf cpg cpi cpn cpo cpp cps cpu cpx cpy cqd cqu cra crb crc crd crf crg crh cri crj crk crl crm crn cro crp crq crr crs crt crv crw crx cry crz csa csb csc csd cse csf csg csh csi csj csk csl csm csn cso csq csr css cst csu csv csw csy csz cta ctc ctd cte ctg cth ctl ctm ctn cto ctp cts ctt ctu ctz cua cub cuc cug cuh cui cuj cuk cul cum cuo cup cuq cur cus cut cuu cuv cuw cux cvg cvn cwa cwb cwd cwe cwg cwt cya cyb cyo czh czk czn czo czt daa dac dad dae daf dag dah dai daj dak dal dam dao dap daq dar das dau dav daw dax day daz dba dbb dbd dbe dbf dbg dbi dbj dbl dbm dbn dbo dbp dbq dbr dbt dbu dbv dbw dby dcc dcr dda ddd dde ddg ddi ddj ddn ddo ddr dds ddw dec ded dee def deg deh dei dek del dem den dep deq der des dev dez dga dgb dgc dgd dge dgg dgh dgi dgk dgl dgn dgo dgr dgs dgt dgu dgw dgx dgz dha dhd dhg dhi dhl dhm dhn dho dhr dhs dhu dhv dhw dhx dia dib dic did dif dig dih dii dij dik dil dim din dio dip diq dir dis dit diu diw dix diy diz dja djb djc djd dje djf dji djj djk djl djm djn djo djr dju djw dka dkk dkl dkr dks dkx dlg dlk dlm dln dma dmb dmc dmd dme dmg dmk dml dmm dmn dmo dmr dms dmu dmv dmw dmx dmy dna dnd dne dng dni dnj dnk dnn dnr dnt dnu dnv dnw dny doa dob doc doe dof doh doi dok dol don doo dop doq dor dos dot dov dow dox doy doz dpp dra drb drc drd dre drg drh dri drl drn dro drq drr drs drt dru drw dry dsb dse dsh dsi dsl dsn dso dsq dta dtb dtd dth dti dtk dtm dto dtp dtr dts dtt dtu dty dua dub duc dud due duf dug duh dui duj duk dul dum dun duo dup duq dur dus duu duv duw dux duy duz dva dwa dwl dwr dws dww dya dyb dyd dyg dyi dym dyn dyo dyu dyy dza dzd dze dzg dzl dzn eaa ebg ebk ebo ebr ebu ecr ecs ecy eee efa efe efi ega egl ego egx egy ehu eip eit eiv eja eka ekc eke ekg eki ekk ekl ekm eko ekp ekr eky ele elh eli elk elm elo elp elu elx ema emb eme emg emi emk emm emn emo emp ems emu emw emx emy ena enb enc end enf enh enm enn eno enq enr enu env enw eot epi era erg erh eri erk ero err ers ert erw ese esh esi esk esl esm esn eso esq ess esu esx etb etc eth etn eto etr ets ett etu etx etz euq eve evh evn ewo ext eya eyo eza eze faa fab fad faf fag fah fai faj fak fal fam fan fap far fat fau fax fay faz fbl fcs fer ffi ffm fgr fia fie fil fip fir fit fiu fiw fkk fkv fla flh fli fll fln flr fly fmp fmu fng fni fod foi fom fon for fos fox fpe fqs frc frd frk frm fro frp frq frr frs frt fse fsl fss fub fuc fud fue fuf fuh fui fuj fum fun fuq fur fut fuu fuv fuy fvr fwa fwe gaa gab gac gad gae gaf gag gah gai gaj gak gal gam gan gao gap gaq gar gas gat gau gav gaw gax gay gaz gba gbb gbc gbd gbe gbf gbg gbh gbi gbj gbk gbl gbm gbn gbo gbp gbq gbr gbs gbu gbv gbw gbx gby gbz gcc gcd gce gcf gcl gcn gcr gct gda gdb gdc gdd gde gdf gdg gdh gdi gdj gdk gdl gdm gdn gdo gdq gdr gds gdt gdu gdx gea geb gec ged geg geh gei gej gek gel gem geq ges gew gex gey gez gfk gft gfx gga ggb ggd gge ggg ggk ggl ggn ggo ggr ggt ggu ggw gha ghc ghe ghh ghk ghl ghn gho ghr ghs ght gia gib gic gid gig gih gil gim gin gio gip giq gir gis git giu giw gix giy giz gji gjk gjm gjn gju gka gke gkn gko gkp glc gld glh gli glj glk gll glo glr glu glw gly gma gmb gmd gme gmh gml gmm gmn gmq gmu gmv gmw gmx gmy gmz gna gnb gnc gnd gne gng gnh gni gnk gnl gnm gnn gno gnq gnr gnt gnu gnw gnz goa gob goc god goe gof gog goh goi goj gok gol gom gon goo gop goq gor gos got gou gow gox goy goz gpa gpe gpn gqa gqi gqn gqr gqu gra grb grc grd grg grh gri grj grk grm gro grq grr grs grt gru grv grw grx gry grz gse gsg gsl gsm gsn gso gsp gss gsw gta gti gtu gua gub guc gud gue guf gug guh gui guk gul gum gun guo gup guq gur gus gut guu guv guw gux guz gva gvc gve gvf gvj gvl gvm gvn gvo gvp gvr gvs gvy gwa gwb gwc gwd gwe gwf gwg gwi gwj gwm gwn gwr gwt gwu gww gwx gxx gya gyb gyd gye gyf gyg gyi gyl gym gyn gyr gyy gza gzi gzn haa hab hac had hae haf hag hah hai haj hak hal ham han hao hap haq har has hav haw hax hay haz hba hbb hbn hbo hbu hca hch hdn hds hdy hea hed heg heh hei hem hgm hgw hhi hhr hhy hia hib hid hif hig hih hii hij hik hil him hio hir hit hiw hix hji hka hke hkk hks hla hlb hld hle hlt hlu hma hmb hmc hmd hme hmf hmg hmh hmi hmj hmk hml hmm hmn hmp hmq hmr hms hmt hmu hmv hmw hmx hmy hmz hna hnd hne hnh hni hnj hnn hno hns hnu hoa hob hoc hod hoe hoh hoi hoj hok hol hom hoo hop hor hos hot hov how hoy hoz hpo hps hra hrc hre hrk hrm hro hrp hrr hrt hru hrw hrx hrz hsb hsh hsl hsn hss hti hto hts htu htx hub huc hud hue huf hug huh hui huj huk hul hum huo hup huq hur hus hut huu huv huw hux huy huz hvc hve hvk hvn hvv hwa hwc hwo hya hyx iai ian iap iar iba ibb ibd ibe ibg ibi ibl ibm ibn ibr ibu iby ica ich icl icr ida idb idc idd ide idi idr ids idt idu ifa ifb ife iff ifk ifm ifu ify igb ige igg igl igm ign igo igs igw ihb ihi ihp ihw iin iir ijc ije ijj ijn ijo ijs ike iki ikk ikl iko ikp ikr ikt ikv ikw ikx ikz ila ilb ilg ili ilk ill ilo ils ilu ilv ilw ima ime imi iml imn imo imr ims imy inb inc ine ing inh inj inl inm inn ino inp ins int inz ior iou iow ipi ipo iqu iqw ira ire irh iri irk irn iro irr iru irx iry isa isc isd ise isg ish isi isk ism isn iso isr ist isu itb itc ite iti itk itl itm ito itr its itt itv itw itx ity itz ium ivb ivv iwk iwm iwo iws ixc ixl iya iyo iyx izh izi izr izz jaa jab jac jad jae jaf jah jaj jak jal jam jan jao jaq jar jas jat jau jax jay jaz jbe jbi jbj jbk jbn jbo jbr jbt jbu jbw jcs jct jda jdg jdt jeb jee jeg jeh jei jek jel jen jer jet jeu jgb jge jgk jgo jhi jhs jia jib jic jid jie jig jih jii jil jim jio jiq jit jiu jiv jiy jjr jkm jko jkp jkr jku jle jls jma jmb jmc jmd jmi jml jmn jmr jms jmw jmx jna jnd jng jni jnj jnl jns job jod jor jos jow jpa jpr jpx jqr jra jrb jrr jrt jru jsl jua jub juc jud juh jui juk jul jum jun juo jup jur jus jut juu juw juy jvd jvn jwi jya jye jyy kaa kab kac kad kae kaf kag kah kai kaj kak kam kao kap kaq kar kav kaw kax kay kba kbb kbc kbd kbe kbf kbg kbh kbi kbj kbk kbl kbm kbn kbo kbp kbq kbr kbs kbt kbu kbv kbw kbx kby kbz kca kcb kcc kcd kce kcf kcg kch kci kcj kck kcl kcm kcn kco kcp kcq kcr kcs kct kcu kcv kcw kcx kcy kcz kda kdc kdd kde kdf kdg kdh kdi kdj kdk kdl kdm kdn kdo kdp kdq kdr kdt kdu kdv kdw kdx kdy kdz kea keb kec ked kee kef keg keh kei kej kek kel kem ken keo kep keq ker kes ket keu kev kew kex key kez kfa kfb kfc kfd kfe kff kfg kfh kfi kfj kfk kfl kfm kfn kfo kfp kfq kfr kfs kft kfu kfv kfw kfx kfy kfz kga kgb kgc kgd kge kgf kgg kgh kgi kgj kgk kgl kgm kgn kgo kgp kgq kgr kgs kgt kgu kgv kgw kgx kgy kha khb khc khd khe khf khg khh khi khj khk khl khn kho khp khq khr khs kht khu khv khw khx khy khz kia kib kic kid kie kif kig kih kii kij kil kim kio kip kiq kis kit kiu kiv kiw kix kiy kiz kja kjb kjc kjd kje kjf kjg kjh kji kjj kjk kjl kjm kjn kjo kjp kjq kjr kjs kjt kju kjx kjy kjz kka kkb kkc kkd kke kkf kkg kkh kki kkj kkk kkl kkm kkn kko kkp kkq kkr kks kkt kku kkv kkw kkx kky kkz kla klb klc kld kle klf klg klh kli klj klk kll klm kln klo klp klq klr kls klt klu klv klw klx kly klz kma kmb kmc kmd kme kmf kmg kmh kmi kmj kmk kml kmm kmn kmo kmp kmq kmr kms kmt kmu kmv kmw kmx kmy kmz kna knb knc knd kne knf kng kni knj knk knl knm knn kno knp knq knr kns knt knu knv knw knx kny knz koa koc kod koe kof kog koh koi koj kok kol koo kop koq kos kot kou kov kow kox koy koz kpa kpb kpc kpd kpe kpf kpg kph kpi kpj kpk kpl kpm kpn kpo kpp kpq kpr kps kpt kpu kpv kpw kpx kpy kpz kqa kqb kqc kqd kqe kqf kqg kqh kqi kqj kqk kql kqm kqn kqo kqp kqq kqr kqs kqt kqu kqv kqw kqx kqy kqz kra krb krc krd kre krf krh kri krj krk krl krm krn kro krp krr krs krt kru krv krw krx kry krz ksa ksb ksc ksd kse ksf ksg ksh ksi ksj ksk ksl ksm ksn kso ksp ksq ksr kss kst ksu ksv ksw ksx ksy ksz kta ktb ktc ktd kte ktf ktg kth kti ktj ktk ktl ktm ktn kto ktp ktq ktr kts ktt ktu ktv ktw ktx kty ktz kub kuc kud kue kuf kug kuh kui kuj kuk kul kum kun kuo kup kuq kus kut kuu kuv kuw kux kuy kuz kva kvb kvc kvd kve kvf kvg kvh kvi kvj kvk kvl kvm kvn kvo kvp kvq kvr kvs kvt kvu kvv kvw kvx kvy kvz kwa kwb kwc kwd kwe kwf kwg kwh kwi kwj kwk kwl kwm kwn kwo kwp kwq kwr kws kwt kwu kwv kww kwx kwy kwz kxa kxb kxc kxd kxe kxf kxh kxi kxj kxk kxl kxm kxn kxo kxp kxq kxr kxs kxt kxu kxv kxw kxx kxy kxz kya kyb kyc kyd kye kyf kyg kyh kyi kyj kyk kyl kym kyn kyo kyp kyq kyr kys kyt kyu kyv kyw kyx kyy kyz kza kzb kzc kzd kze kzf kzg kzh kzi kzj kzk kzl kzm kzn kzo kzp kzq kzr kzs kzt kzu kzv kzw kzx kzy kzz laa lab lac lad lae laf lag lah lai laj lak lal lam lan lap laq lar las lau law lax lay laz lba lbb lbc lbe lbf lbg lbi lbj lbk lbl lbm lbn lbo lbq lbr lbs lbt lbu lbv lbw lbx lby lbz lcc lcd lce lcf lch lcl lcm lcp lcq lcs lda ldb ldd ldg ldh ldi ldj ldk ldl ldm ldn ldo ldp ldq lea leb lec led lee lef leg leh lei lej lek lel lem len leo lep leq ler les let leu lev lew lex ley lez lfa lfn lga lgb lgg lgh lgi lgk lgl lgm lgn lgq lgr lgt lgu lgz lha lhh lhi lhl lhm lhn lhp lhs lht lhu lia lib lic lid lie lif lig lih lii lij lik lil lio lip liq lir lis liu liv liw lix liy liz lja lje lji ljl ljp ljw ljx lka lkb lkc lkd lke lkh lki lkj lkl lkm lkn lko lkr lks lkt lku lky lla llb llc lld lle llf llg llh lli llj llk lll llm lln llo llp llq lls llu llx lma lmb lmc lmd lme lmf lmg lmh lmi lmj lmk lml lmm lmn lmo lmp lmq lmr lmu lmv lmw lmx lmy lmz lna lnb lnd lng lnh lni lnj lnl lnm lnn lno lns lnu lnw lnz loa lob loc loe lof log loh loi loj lok lol lom lon loo lop loq lor los lot lou lov low lox loy loz lpa lpe lpn lpo lpx lra lrc lre lrg lri lrk lrl lrm lrn lro lrr lrt lrv lrz lsa lsd lse lsg lsh lsi lsl lsm lso lsp lsr lss lst lsy ltc ltg lti ltn lto lts ltu lua luc lud lue luf lui luj luk lul lum lun luo lup luq lur lus lut luu luv luw luy luz lva lvk lvs lvu lwa lwe lwg lwh lwl lwm lwo lwt lwu lww lya lyg lyn lzh lzl lzn lzz maa mab mad mae maf mag mai maj mak mam man map maq mas mat mau mav maw max maz mba mbb mbc mbd mbe mbf mbh mbi mbj mbk mbl mbm mbn mbo mbp mbq mbr mbs mbt mbu mbv mbw mbx mby mbz mca mcb mcc mcd mce mcf mcg mch mci mcj mck mcl mcm mcn mco mcp mcq mcr mcs mct mcu mcv mcw mcx mcy mcz mda mdb mdc mdd mde mdf mdg mdh mdi mdj mdk mdl mdm mdn mdp mdq mdr mds mdt mdu mdv mdw mdx mdy mdz mea meb mec med mee mef meg meh mei mej mek mel mem men meo mep meq mer mes met meu mev mew mey mez mfa mfb mfc mfd mfe mff mfg mfh mfi mfj mfk mfl mfm mfn mfo mfp mfq mfr mfs mft mfu mfv mfw mfx mfy mfz mga mgb mgc mgd mge mgf mgg mgh mgi mgj mgk mgl mgm mgn mgo mgp mgq mgr mgs mgt mgu mgv mgw mgx mgy mgz mha mhb mhc mhd mhe mhf mhg mhh mhi mhj mhk mhl mhm mhn mho mhp mhq mhr mhs mht mhu mhw mhx mhy mhz mia mib mic mid mie mif mig mih mii mij mik mil mim min mio mip miq mir mis mit miu miw mix miy miz mja mjc mjd mje mjg mjh mji mjj mjk mjl mjm mjn mjo mjp mjq mjr mjs mjt mju mjv mjw mjx mjy mjz mka mkb mkc mke mkf mkg mkh mki mkj mkk mkl mkm mkn mko mkp mkq mkr mks mkt mku mkv mkw mkx mky mkz mla mlb mlc mld mle mlf mlh mli mlj mlk mll mlm mln mlo mlp mlq mlr mls mlu mlv mlw mlx mlz mma mmb mmc mmd mme mmf mmg mmh mmi mmj mmk mml mmm mmn mmo mmp mmq mmr mmt mmu mmv mmw mmx mmy mmz mna mnb mnc mnd mne mnf mng mnh mni mnj mnk mnl mnm mnn mno mnp mnq mnr mns mnt mnu mnv mnw mnx mny mnz moa moc mod moe mof mog moh moi moj mok mom moo mop moq mor mos mot mou mov mow mox moy moz mpa mpb mpc mpd mpe mpg mph mpi mpj mpk mpl mpm mpn mpo mpp mpq mpr mps mpt mpu mpv mpw mpx mpy mpz mqa mqb mqc mqe mqf mqg mqh mqi mqj mqk mql mqm mqn mqo mqp mqq mqr mqs mqt mqu mqv mqw mqx mqy mqz mra mrb mrc mrd mre mrf mrg mrh mrj mrk mrl mrm mrn mro mrp mrq mrr mrs mrt mru mrv mrw mrx mry mrz msb msc msd mse msf msg msh msi msj msk msl msm msn mso msp msq msr mss mst msu msv msw msx msy msz mta mtb mtc mtd mte mtf mtg mth mti mtj mtk mtl mtm mtn mto mtp mtq mtr mts mtt mtu mtv mtw mtx mty mua mub muc mud mue mug muh mui muj muk mul mum mun muo mup muq mur mus mut muu muv mux muy muz mva mvb mvd mve mvf mvg mvh mvi mvk mvl mvm mvn mvo mvp mvq mvr mvs mvt mvu mvv mvw mvx mvy mvz mwa mwb mwc mwd mwe mwf mwg mwh mwi mwj mwk mwl mwm mwn mwo mwp mwq mwr mws mwt mwu mwv mww mwx mwy mwz mxa mxb mxc mxd mxe mxf mxg mxh mxi mxj mxk mxl mxm mxn mxo mxp mxq mxr mxs mxt mxu mxv mxw mxx mxy mxz myb myc myd mye myf myg myh myi myj myk myl mym myn myo myp myq myr mys myt myu myv myw myx myy myz mza mzb mzc mzd mze mzg mzh mzi mzj mzk mzl mzm mzn mzo mzp mzq mzr mzs mzt mzu mzv mzw mzx mzy mzz naa nab nac nad nae naf nag nah nai naj nak nal nam nan nao nap naq nar nas nat naw nax nay naz nba nbb nbc nbd nbe nbf nbg nbh nbi nbj nbk nbm nbn nbo nbp nbq nbr nbs nbt nbu nbv nbw nbx nby nca ncb ncc ncd nce ncf ncg nch nci ncj nck ncl ncm ncn nco ncp ncr ncs nct ncu ncx ncz nda ndb ndc ndd ndf ndg ndh ndi ndj ndk ndl ndm ndn ndp ndq ndr nds ndt ndu ndv ndw ndx ndy ndz nea neb nec ned nee nef neg neh nei nej nek nem nen neo neq ner nes net neu nev new nex ney nez nfa nfd nfl nfr nfu nga ngb ngc ngd nge ngf ngg ngh ngi ngj ngk ngl ngm ngn ngo ngp ngq ngr ngs ngt ngu ngv ngw ngx ngy ngz nha nhb nhc nhd nhe nhf nhg nhh nhi nhk nhm nhn nho nhp nhq nhr nht nhu nhv nhw nhx nhy nhz nia nib nic nid nie nif nig nih nii nij nik nil nim nin nio niq nir nis nit niu niv niw nix niy niz nja njb njd njh nji njj njl njm njn njo njr njs njt nju njx njy njz nka nkb nkc nkd nke nkf nkg nkh nki nkj nkk nkm nkn nko nkp nkq nkr nks nkt nku nkv nkw nkx nkz nla nlc nle nlg nli nlj nlk nll nln nlo nlq nlr nlu nlv nlw nlx nly nlz nma nmb nmc nmd nme nmf nmg nmh nmi nmj nmk nml nmm nmn nmo nmp nmq nmr nms nmt nmu nmv nmw nmx nmy nmz nna nnb nnc nnd nne nnf nng nnh nni nnj nnk nnl nnm nnn nnp nnq nnr nns nnt nnu nnv nnw nnx nny nnz noa noc nod noe nof nog noh noi noj nok nol nom non noo nop noq nos not nou nov now noy noz npa npb npg nph npi npl npn npo nps npu npy nqg nqk nqm nqn nqo nqq nqy nra nrb nrc nre nrg nri nrk nrl nrm nrn nrp nrr nrt nru nrx nrz nsa nsc nsd nse nsf nsg nsh nsi nsk nsl nsm nsn nso nsp nsq nsr nss nst nsu nsv nsw nsx nsy nsz nte ntg nti ntj ntk ntm nto ntp ntr nts ntu ntw ntx nty ntz nua nub nuc nud nue nuf nug nuh nui nuj nuk nul num nun nuo nup nuq nur nus nut nuu nuv nuw nux nuy nuz nvh nvm nvo nwa nwb nwc nwe nwg nwi nwm nwo nwr nwx nwy nxa nxd nxe nxg nxi nxk nxl nxm nxn nxq nxr nxu nxx nyb nyc nyd nye nyf nyg nyh nyi nyj nyk nyl nym nyn nyo nyp nyq nyr nys nyt nyu nyv nyw nyx nyy nza nzb nzi nzk nzm nzs nzu nzy nzz oaa oac oar oav obi obk obl obm obo obr obt obu oca och oco ocu oda odk odt odu ofo ofs ofu ogb ogc oge ogg ogo ogu oht ohu oia oin ojb ojc ojg ojp ojs ojv ojw oka okb okd oke okg okh oki okj okk okl okm okn oko okr oks oku okv okx ola old ole olk olm olo olr oma omb omc ome omg omi omk oml omn omo omp omq omr omt omu omv omw omx ona onb one ong oni onj onk onn ono onp onr ons ont onu onw onx ood oog oon oor oos opa opk opm opo opt opy ora orc ore org orh orn oro orr ors ort oru orv orw orx ory orz osa osc osi oso osp ost osu osx ota otb otd ote oti otk otl otm otn oto otq otr ots ott otu otw otx oty otz oua oub oue oui oum oun owi owl oyb oyd oym oyy ozm paa pab pac pad pae paf pag pah pai pak pal pam pao pap paq par pas pat pau pav paw pax pay paz pbb pbc pbe pbf pbg pbh pbi pbl pbn pbo pbp pbr pbs pbt pbu pbv pby pbz pca pcb pcc pcd pce pcf pcg pch pci pcj pck pcl pcm pcn pcp pcr pcw pda pdc pdi pdn pdo pdt pdu pea peb ped pee pef peg peh pei pej pek pel pem peo pep peq pes pev pex pey pez pfa pfe pfl pga pgg pgi pgk pgl pgn pgs pgu pgy pha phd phg phh phi phk phl phm phn pho phq phr pht phu phv phw pia pib pic pid pie pif pig pih pii pij pil pim pin pio pip pir pis pit piu piv piw pix piy piz pjt pka pkb pkc pkg pkh pkn pko pkp pkr pks pkt pku pla plb plc pld ple plf plg plh plj plk pll pln plo plp plq plr pls plt plu plv plw ply plz pma pmb pmc pmd pme pmf pmh pmi pmj pmk pml pmm pmn pmo pmq pmr pms pmt pmu pmw pmx pmy pmz pna pnb pnc pne png pnh pni pnj pnk pnl pnm pnn pno pnp pnq pnr pns pnt pnu pnv pnw pnx pny pnz poc pod poe pof pog poh poi pok pom pon poo pop poq pos pot pov pow pox poy poz ppa ppe ppi ppk ppl ppm ppn ppo ppp ppq ppr pps ppt ppu pqa pqe pqm pqw pra prb prc prd pre prf prg prh pri prk prl prm prn pro prp prq prr prs prt pru prw prx pry prz psa psc psd pse psg psh psi psl psm psn pso psp psq psr pss pst psu psw psy pta pth pti ptn pto ptp ptr ptt ptu ptv ptw pty pua pub puc pud pue puf pug pui puj puk pum puo pup puq pur put puu puw pux puy puz pwa pwb pwg pwi pwm pwn pwo pwr pww pxm pye pym pyn pys pyu pyx pyy pzn qaa..qtz qua qub quc qud quf qug quh qui quk qul qum qun qup quq qur qus quv quw qux quy quz qva qvc qve qvh qvi qvj qvl qvm qvn qvo qvp qvs qvw qvy qvz qwa qwc qwe qwh qwm qws qwt qxa qxc qxh qxl qxn qxo qxp qxq qxr qxs qxt qxu qxw qya qyp raa rab rac rad raf rag rah rai raj rak ral ram ran rao rap raq rar ras rat rau rav raw rax ray raz rbb rbk rbl rbp rcf rdb rea reb ree reg rei rej rel rem ren rer res ret rey rga rge rgk rgn rgr rgs rgu rhg rhp ria rie rif ril rim rin rir rit riu rjg rji rjs rka rkb rkh rki rkm rkt rkw rma rmb rmc rmd rme rmf rmg rmh rmi rmk rml rmm rmn rmo rmp rmq rmr rms rmt rmu rmv rmw rmx rmy rmz rna rnd rng rnl rnn rnp rnr rnw roa rob roc rod roe rof rog rol rom roo rop ror rou row rpn rpt rri rro rrt rsb rsi rsl rtc rth rtm rtw rub ruc rue ruf rug ruh rui ruk ruo rup ruq rut ruu ruy ruz rwa rwk rwm rwo rwr rxd rxw ryn rys ryu saa sab sac sad sae saf sah sai saj sak sal sam sao sap saq sar sas sat sau sav saw sax say saz sba sbb sbc sbd sbe sbf sbg sbh sbi sbj sbk sbl sbm sbn sbo sbp sbq sbr sbs sbt sbu sbv sbw sbx sby sbz sca scb sce scf scg sch sci sck scl scn sco scp scq scs scu scv scw scx sda sdb sdc sde sdf sdg sdh sdj sdk sdl sdm sdn sdo sdp sdr sds sdt sdu sdv sdx sdz sea seb sec sed see sef seg seh sei sej sek sel sem sen seo sep seq ser ses set seu sev sew sey sez sfb sfe sfm sfs sfw sga sgb sgc sgd sge sgg sgh sgi sgj sgk sgl sgm sgn sgo sgp sgr sgs sgt sgu sgw sgx sgy sgz sha shb shc shd she shg shh shi shj shk shl shm shn sho shp shq shr shs sht shu shv shw shx shy shz sia sib sid sie sif sig sih sii sij sik sil sim sio sip siq sir sis sit siu siv siw six siy siz sja sjb sjd sje sjg sjk sjl sjm sjn sjo sjp sjr sjs sjt sju sjw ska skb skc skd ske skf skg skh ski skj skk skm skn sko skp skq skr sks skt sku skv skw skx sky skz sla slc sld sle slf slg slh sli slj sll slm sln slp slq slr sls slt slu slw slx sly slz sma smb smc smd smf smg smh smi smj smk sml smm smn smp smq smr sms smt smu smv smw smx smy smz snb snc sne snf sng snh sni snj snk snl snm snn sno snp snq snr sns snu snv snw snx sny snz soa sob soc sod soe sog soh soi soj sok sol son soo sop soq sor sos sou sov sow sox soy soz spb spc spd spe spg spi spk spl spm spo spp spq spr sps spt spu spv spx spy sqa sqh sqj sqk sqm sqn sqo sqq sqr sqs sqt squ sra srb src sre srf srg srh sri srk srl srm srn sro srq srr srs srt sru srv srw srx sry srz ssa ssb ssc ssd sse ssf ssg ssh ssi ssj ssk ssl ssm ssn sso ssp ssq ssr sss sst ssu ssv ssx ssy ssz sta stb std ste stf stg sth sti stj stk stl stm stn sto stp stq str sts stt stu stv stw sty sua sub suc sue sug sui suj suk sul sum suq sur sus sut suv suw sux suy suz sva svb svc sve svk svm svr svs svx swb swc swf swg swh swi swj swk swl swm swn swo swp swq swr sws swt swu swv sww swx swy sxb sxc sxe sxg sxk sxl sxm sxn sxo sxr sxs sxu sxw sya syb syc syd syi syk syl sym syn syo syr sys syw syy sza szb szc szd sze szg szl szn szp szv szw taa tab tac tad tae taf tag tai taj tak tal tan tao tap taq tar tas tau tav taw tax tay taz tba tbb tbc tbd tbe tbf tbg tbh tbi tbj tbk tbl tbm tbn tbo tbp tbq tbr tbs tbt tbu tbv tbw tbx tby tbz tca tcb tcc tcd tce tcf tcg tch tci tck tcl tcm tcn tco tcp tcq tcs tct tcu tcw tcx tcy tcz tda tdb tdc tdd tde tdf tdg tdh tdi tdj tdk tdl tdn tdo tdq tdr tds tdt tdu tdv tdx tdy tea teb tec ted tee tef teg teh tei tek tem ten teo tep teq ter tes tet teu tev tew tex tey tfi tfn tfo tfr tft tga tgb tgc tgd tge tgf tgg tgh tgi tgj tgn tgo tgp tgq tgr tgs tgt tgu tgv tgw tgx tgy tgz thc thd the thf thh thi thk thl thm thn thp thq thr ths tht thu thv thw thx thy thz tia tic tid tie tif tig tih tii tij tik til tim tin tio tip tiq tis tit tiu tiv tiw tix tiy tiz tja tjg tji tjl tjm tjn tjo tjs tju tjw tka tkb tkd tke tkf tkg tkk tkl tkm tkn tkp tkq tkr tks tkt tku tkw tkx tkz tla tlb tlc tld tlf tlg tlh tli tlj tlk tll tlm tln tlo tlp tlq tlr tls tlt tlu tlv tlw tlx tly tma tmb tmc tmd tme tmf tmg tmh tmi tmj tmk tml tmm tmn tmo tmp tmq tmr tms tmt tmu tmv tmw tmy tmz tna tnb tnc tnd tne tnf tng tnh tni tnk tnl tnm tnn tno tnp tnq tnr tns tnt tnu tnv tnw tnx tny tnz tob toc tod toe tof tog toh toi toj tol tom too top toq tor tos tou tov tow tox toy toz tpa tpc tpe tpf tpg tpi tpj tpk tpl tpm tpn tpo tpp tpq tpr tpt tpu tpv tpw tpx tpy tpz tqb tql tqm tqn tqo tqp tqq tqr tqt tqu tqw tra trb trc trd tre trf trg trh tri trj trk trl trm trn tro trp trq trr trs trt tru trv trw trx try trz tsa tsb tsc tsd tse tsf tsg tsh tsi tsj tsk tsl tsm tsp tsq tsr tss tst tsu tsv tsw tsx tsy tsz tta ttb ttc ttd tte ttf ttg tth tti ttj ttk ttl ttm ttn tto ttp ttq ttr tts ttt ttu ttv ttw tty ttz tua tub tuc tud tue tuf tug tuh tui tuj tul tum tun tuo tup tuq tus tut tuu tuv tuw tux tuy tuz tva tvd tve tvk tvl tvm tvn tvo tvs tvt tvu tvw tvy twa twb twc twd twe twf twg twh twl twm twn two twp twq twr twt twu tww twx twy txa txb txc txe txg txh txi txm txn txo txq txr txs txt txu txx txy tya tye tyh tyi tyj tyl tyn typ tyr tys tyt tyu tyv tyx tyz tza tzh tzj tzl tzm tzn tzo tzx uam uan uar uba ubi ubl ubr ubu uby uda ude udg udi udj udl udm udu ues ufi uga ugb uge ugn ugo ugy uha uhn uis uiv uji uka ukg ukh ukl ukp ukq uks uku ukw uky ula ulb ulc ule ulf uli ulk ull ulm uln ulu ulw uma umb umc umd umg umi umm umn umo ump umr ums umu una und une ung unk unm unn unp unr unu unx unz uok upi upv ura urb urc ure urf urg urh uri urj urk url urm urn uro urp urr urt uru urv urw urx ury urz usa ush usi usk usp usu uta ute utp utr utu uum uun uur uuu uve uvh uvl uwa uya uzn uzs vaa vae vaf vag vah vai vaj val vam van vao vap var vas vau vav vay vbb vbk vec ved vel vem veo vep ver vgr vgt vic vid vif vig vil vin vis vit viv vka vki vkj vkk vkl vkm vko vkp vkt vku vlp vls vma vmb vmc vmd vme vmf vmg vmh vmi vmj vmk vml vmm vmp vmq vmr vms vmu vmv vmw vmx vmy vmz vnk vnm vnp vor vot vra vro vrs vrt vsi vsl vsv vto vum vun vut vwa waa wab wac wad wae waf wag wah wai waj wak wal wam wan wao wap waq war was wat wau wav waw wax way waz wba wbb wbe wbf wbh wbi wbj wbk wbl wbm wbp wbq wbr wbt wbv wbw wca wci wdd wdg wdj wdk wdu wdy wea wec wed weg weh wei wem wen weo wep wer wes wet weu wew wfg wga wgb wgg wgi wgo wgu wgw wgy wha whg whk whu wib wic wie wif wig wih wii wij wik wil wim win wir wit wiu wiv wiw wiy wja wji wka wkb wkd wkl wku wkw wky wla wlc wle wlg wli wlk wll wlm wlo wlr wls wlu wlv wlw wlx wly wma wmb wmc wmd wme wmh wmi wmm wmn wmo wms wmt wmw wmx wnb wnc wnd wne wng wni wnk wnm wnn wno wnp wnu wnw wny woa wob woc wod woe wof wog woi wok wom won woo wor wos wow woy wpc wra wrb wrd wrg wrh wri wrk wrl wrm wrn wro wrp wrr wrs wru wrv wrw wrx wry wrz wsa wsi wsk wsr wss wsu wsv wtf wth wti wtk wtm wtw wua wub wud wuh wul wum wun wur wut wuu wuv wux wuy wwa wwb wwo wwr www wxa wxw wya wyb wyi wym wyr wyy xaa xab xac xad xae xag xai xal xam xan xao xap xaq xar xas xat xau xav xaw xay xba xbb xbc xbd xbe xbg xbi xbj xbm xbn xbo xbp xbr xbw xbx xby xcb xcc xce xcg xch xcl xcm xcn xco xcr xct xcu xcv xcw xcy xda xdc xdk xdm xdy xeb xed xeg xel xem xep xer xes xet xeu xfa xga xgb xgd xgf xgg xgi xgl xgm xgn xgr xgu xgw xha xhc xhd xhe xhr xht xhu xhv xia xib xii xil xin xip xir xiv xiy xjb xjt xka xkb xkc xkd xke xkf xkg xkh xki xkj xkk xkl xkn xko xkp xkq xkr xks xkt xku xkv xkw xkx xky xkz xla xlb xlc xld xle xlg xli xln xlo xlp xls xlu xly xma xmb xmc xmd xme xmf xmg xmh xmj xmk xml xmm xmn xmo xmp xmq xmr xms xmt xmu xmv xmw xmx xmy xmz xna xnb xnd xng xnh xni xnk xnn xno xnr xns xnt xnu xny xnz xoc xod xog xoi xok xom xon xoo xop xor xow xpa xpc xpe xpg xpi xpj xpk xpm xpn xpo xpp xpq xpr xps xpt xpu xpy xqa xqt xra xrb xrd xre xrg xri xrm xrn xrq xrr xrt xru xrw xsa xsb xsc xsd xse xsh xsi xsj xsl xsm xsn xso xsp xsq xsr xss xsu xsv xsy xta xtb xtc xtd xte xtg xth xti xtj xtl xtm xtn xto xtp xtq xtr xts xtt xtu xtv xtw xty xtz xua xub xud xug xuj xul xum xun xuo xup xur xut xuu xve xvi xvn xvo xvs xwa xwc xwd xwe xwg xwj xwk xwl xwo xwr xwt xww xxb xxk xxm xxr xxt xya xyb xyj xyk xyl xyt xyy xzh xzm xzp yaa yab yac yad yae yaf yag yah yai yaj yak yal yam yan yao yap yaq yar yas yat yau yav yaw yax yay yaz yba ybb ybd ybe ybh ybi ybj ybk ybl ybm ybn ybo ybx yby ych ycl ycn ycp yda ydd yde ydg ydk yds yea yec yee yei yej yel yen yer yes yet yeu yev yey yga ygi ygl ygm ygp ygr ygu ygw yha yhd yhl yia yif yig yih yii yij yik yil yim yin yip yiq yir yis yit yiu yiv yix yiy yiz yka ykg yki ykk ykl ykm ykn yko ykr ykt yku yky yla ylb yle ylg yli yll ylm yln ylo ylr ylu yly yma ymb ymc ymd yme ymg ymh ymi ymk yml ymm ymn ymo ymp ymq ymr yms ymt ymx ymz yna ynd yne yng ynh ynk ynl ynn yno ynq yns ynu yob yog yoi yok yol yom yon yos yot yox yoy ypa ypb ypg yph ypk ypm ypn ypo ypp ypz yra yrb yre yri yrk yrl yrm yrn yrs yrw yry ysc ysd ysg ysl ysn yso ysp ysr yss ysy yta ytl ytp ytw yty yua yub yuc yud yue yuf yug yui yuj yuk yul yum yun yup yuq yur yut yuu yuw yux yuy yuz yva yvt ywa ywg ywl ywn ywq ywr ywt ywu yww yxa yxg yxl yxm yxu yxy yyr yyu yyz yzg yzk zaa zab zac zad zae zaf zag zah zai zaj zak zal zam zao zap zaq zar zas zat zau zav zaw zax zay zaz zbc zbe zbl zbt zbw zca zch zdj zea zeg zeh zen zga zgb zgh zgm zgn zgr zhb zhd zhi zhn zhw zhx zia zib zik zil zim zin zir ziw ziz zka zkb zkd zkg zkh zkk zkn zko zkp zkr zkt zku zkv zkz zle zlj zlm zln zlq zls zlw zma zmb zmc zmd zme zmf zmg zmh zmi zmj zmk zml zmm zmn zmo zmp zmq zmr zms zmt zmu zmv zmw zmx zmy zmz zna znd zne zng znk zns zoc zoh zom zoo zoq zor zos zpa zpb zpc zpd zpe zpf zpg zph zpi zpj zpk zpl zpm zpn zpo zpp zpq zpr zps zpt zpu zpv zpw zpx zpy zpz zqe zra zrg zrn zro zrp zrs zsa zsk zsl zsm zsr zsu zte ztg ztl ztm ztn ztp ztq zts ztt ztu ztx zty zua zuh zum zun zuy zwa zxx zyb zyg zyj zyn zyp zza zzj aao abh abv acm acq acw acx acy adf ads aeb aec aed aen afb afg ajp apc apd arb arq ars ary arz ase asf asp asq asw auz avl ayh ayl ayn ayp bbz bfi bfk bjn bog bqn bqy btj bve bvl bvu bzs cdo cds cjy cmn coa cpx csc csd cse csf csg csl csn csq csr czh czo doq dse dsl dup ecs esl esn eso eth fcs fse fsl fss gan gds gom gse gsg gsm gss gus hab haf hak hds hji hks hos hps hsh hsl hsn icl ils inl ins ise isg isr jak jax jcs jhs jls jos jsl jus kgi knn kvb kvk kvr kxd lbs lce lcf liw lls lsg lsl lso lsp lst lsy ltg lvs lzh max mdl meo mfa mfb mfs min mnp mqg mre msd msi msr mui mzc mzg mzy nan nbs ncs nsi nsl nsp nsr nzs okl orn ors pel pga pks prl prz psc psd pse psg psl pso psp psr pys rms rsi rsl sdl sfb sfs sgg sgx shu slf sls sqk sqs ssh ssp ssr svk swc swh swl syy tmw tse tsm tsq tss tsy tza ugn ugy ukl uks urk uzn uzs vgt vkk vkt vsi vsl vsv wuu xki xml xmm xms yds ysl yue zib zlm zmi zsl zsm afak aghb arab armi armn avst bali bamu bass batk beng blis bopo brah brai bugi buhd cakm cans cari cham cher cirt copt cprt cyrl cyrs deva dsrt dupl egyd egyh egyp elba ethi geok geor glag goth gran grek gujr guru hang hani hano hans hant hebr hira hluw hmng hrkt hung inds ital java jpan jurc kali kana khar khmr khoj knda kore kpel kthi lana laoo latf latg latn lepc limb lina linb lisu loma lyci lydi mahj mand mani maya mend merc mero mlym mong moon mroo mtei mymr narb nbat nkgb nkoo nshu ogam olck orkh orya osma palm perm phag phli phlp phlv phnx plrd prti qaaa..qabx rjng roro runr samr sara sarb saur sgnw shaw shrd sind sinh sora sund sylo syrc syre syrj syrn tagb takr tale talu taml tang tavt telu teng tfng tglg thaa thai tibt tirh ugar vaii visp wara wole xpeo xsux yiii zinh zmth zsym zxxx zyyy zzzz aa ac ad ae af ag ai al am an ao aq ar as at au aw ax az ba bb bd be bf bg bh bi bj bl bm bn bo bq br bs bt bu bv bw by bz ca cc cd cf cg ch ci ck cl cm cn co cp cr cs cu cv cw cx cy cz dd de dg dj dk dm do dz ea ec ee eg eh er es et eu fi fj fk fm fo fr fx ga gb gd ge gf gg gh gi gl gm gn gp gq gr gs gt gu gw gy hk hm hn hr ht hu ic id ie il im in io iq ir is it je jm jo jp ke kg kh ki km kn kp kr kw ky kz la lb lc li lk lr ls lt lu lv ly ma mc md me mf mg mh mk ml mm mn mo mp mq mr ms mt mu mv mw mx my mz na nc ne nf ng ni nl no np nr nt nu nz om pa pe pf pg ph pk pl pm pn pr ps pt pw py qa qm..qz re ro rs ru rw sa sb sc sd se sg sh si sj sk sl sm sn so sr ss st su sv sx sy sz ta tc td tf tg th tj tk tl tm tn to tp tr tt tv tw tz ua ug um us uy uz va vc ve vg vi vn vu wf ws xa..xz yd ye yt yu za zm zr zw zz 001 002 003 005 009 011 013 014 015 017 018 019 021 029 030 034 035 039 053 054 057 061 142 143 145 150 151 154 155 419 1606nict 1694acad 1901 1959acad 1994 1996 alalc97 aluku arevela arevmda baku1926 bauddha biscayan biske bohoric boont dajnko emodeng fonipa fonupa fonxsamp hepburn heploc hognorsk itihasa jauer jyutping kkcor kscor laukika lipaw luna1918 metelko monoton ndyuka nedis njiva nulik osojs pamaka petr1708 pinyin polyton puter rigik rozaj rumgr scotland scouse solba surmiran sursilv sutsilv tarask uccor ucrcor ulster unifon vaidika valencia vallader wadegile ",


         tags : "art-lojban cel-gaulish en-gb-oed i-ami i-bnn i-default i-enochian i-hak i-klingon i-lux i-mingo i-navajo i-pwn i-tao i-tay i-tsu no-bok no-nyn sgn-be-fr sgn-be-nl sgn-ch-de zh-guoyu zh-hakka zh-min zh-min-nan zh-xiang az-arab az-cyrl az-latn be-latn bs-cyrl bs-latn de-1901 de-1996 de-at-1901 de-at-1996 de-ch-1901 de-ch-1996 de-de-1901 de-de-1996 en-boont en-scouse es-419 iu-cans iu-latn mn-cyrl mn-mong sgn-br sgn-co sgn-de sgn-dk sgn-es sgn-fr sgn-gb sgn-gr sgn-ie sgn-it sgn-jp sgn-mx sgn-ni sgn-nl sgn-no sgn-pt sgn-se sgn-us sgn-za sl-nedis sl-rozaj sr-cyrl sr-latn tg-arab tg-cyrl uz-cyrl uz-latn yi-latn zh-cmn zh-cmn-hans zh-cmn-hant zh-gan zh-hans zh-hans-cn zh-hans-hk zh-hans-mo zh-hans-sg zh-hans-tw zh-hant zh-hant-cn zh-hant-hk zh-hant-mo zh-hant-sg zh-hant-tw zh-wuu zh-yue "


};
/*
 * Copyright 2011-2013 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


/* ---------------------------------------------------------------- */
/*              ARIA Defintions and Validation Methods              */
/* ---------------------------------------------------------------- */


if (typeof OpenAjax.a11y.aria == "undefined") {
	OpenAjax.a11y.aria = {
			
		/*
		 * array of WAI-ARIA global states and properties
		 * @see http://www.w3.org/TR/wai-aria/#global_states
		 */
		globalProperties : [
            "aria-atomic", 
            "aria-busy", 
            "aria-controls", 
            "aria-describedby",
            "aria-disabled", 
            "aria-dropeffect", 
            "aria-flowto", 
            "aria-grabbed",
            "aria-haspopup", 
            "aria-hidden", 
            "aria-invalid", 
            "aria-label",
            "aria-labelledby", 
            "aria-live", 
            "aria-owns", 
            "aria-relevant"
        ],
	
        /*
         * XSD data types for all WAI-ARIA properties
         * along with valid values when the data type is NMTOKEN
         */
        propertyDataTypes : {
         	"aria-activedescendant" : {
         		type : "idref"
         	},
         	"aria-atomic" : {
         		type : "boolean"
         	},
         	"aria-autocomplete" : {
         		type : "nmtoken",
         		values : ["inline", "list", "both", "none"]
         	},
         	"aria-busy" : {
         		type : "boolean"
         	},
         	"aria-checked" : {
         		type : "nmtoken",
         		values : ["true", "false", "mixed", "undefined"]
         	},
         	"aria-controls" : {
         		type : "idrefs"
         	},
         	"aria-describedby" : {
         		type : "idrefs"
         	},
         	"aria-disabled" : {
         		type : "boolean"
         	},
         	"aria-dropeffect" : {
         		type : "nmtokens",
         		values : ["copy", "move", "link", "execute", "popup", "none"]
         	},
         	"aria-expanded" : {
         		type : "nmtoken",
         		values : ["true", "false", "undefined"]
         	},
         	"aria-flowto" : {
         		type : "idrefs"
         	},
         	"aria-grabbed" : {
         		type : "nmtoken",
         		values : ["true", "false", "undefined"]
         	},
         	"aria-haspopup" : {
         		type : "boolean"
         	},
         	"aria-hidden" : {
         		type : "boolean"
         	},
         	"aria-invalid" : {
         		type : "nmtoken",
         		values : ["true", "false", "spelling", "grammar"]
         	},
         	"aria-label" : {
         		type : "string"
         	},
         	"aria-labelledby" : {
         		type : "idrefs"
         	},
         	"aria-level" : {
         		type : "integer"
         	},
         	"aria-live" : {
         		type : "nmtoken",
         		values : ["off", "polite", "assertive"]
         	},
         	"aria-multiline" : {
         		type : "boolean"
         	},
         	"aria-multiselectable" : {
         		type : "boolean"
         	},
         	"aria-orientation" : {
         		type : "nmtoken",
         		values : ["vertical", "horizontal"]
         	},
         	"aria-owns" : {
         		type : "idrefs"
         	},
         	"aria-posinset" : {
         		type : "integer"
         	},
         	"aria-pressed" : {
         		type : "nmtoken",
         		values : ["true", "false", "mixed", "undefined"]
         	},
         	"aria-readonly" : {
         		type : "boolean"
         	},
         	"aria-relevant" : {
         		type : "nmtokens",
         		values : ["additions", "removals", "text", "all"]
         	},
         	"aria-required" : {
         		type : "boolean"
         	},
         	"aria-selected" : {
         		type : "nmtoken",
         		values : ["true", "false", "undefined"]
         	},
         	"aria-setsize" : {
         		type : "integer"
         	},
         	"aria-sort" : {
         		type : "nmtoken",
         		values : ["ascending", "descending", "other", "none"]
         	},
         	"aria-valuemax" : {
         		type : "decimal"
         	},
         	"aria-valuemin" : {
         		type : "decimal"
         	},
         	"aria-valuenow" : {
         		type : "decimal"
         	},
         	"aria-valuetext" : {
         		type : "string"
         	}
        },
        
        /*
         * list of abstract roles - used to support the WAI-ARIA role taxonomy and 
         * not to be used by content authors
         * @see http://www.w3.org/TR/wai-aria/roles#isAbstract
         */
        abstractRoles : [
            "command", 
            "composite", 
            "input", 
            "landmark", 
            "range",
            "roletype", 
            "section", 
            "sectionhead", 
            "select",
            "structure", 
            "widget", 
            "window"
        ],
                         
          /*
           * design patterns for concrete WAI-ARIA roles
           * legitimate keys for each role include:
           * 
           * - container: appropriate container(s) for that role
           * - props: states and properties that may be associated with this role (in addition to the global states and properties listed above)
           * - reqProps: required states or properties for this role
           * - reqChildren: required children for this role
           * - htmlEquiv: HTML equivalent for this role
           * - roleType: one of widget, landmark, or null 
           */
        designPatterns : {
         		
         	"alert" : {
         		reqName : false,
         		container : null,
         		props : ["aria-expanded"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: false,
         		roleType : "live"     		
         	},
         	                
         	"alertdialog" : {
         		reqName : true,
         		container : null,
         		props : ["aria-expanded"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: false,
         		supportOnClick: true,
         		roleType : "widget"
         	},
         	
         	"application" : {
         		reqName : true,
         		container : null,
         		props : ["aria-expanded"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: false,
         		roleType : "landmark"
         	},
         	
         	"article" : {
         		reqName : false,
         		container : null,
         		props : ["aria-expanded"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : null,
          		nameFromContent: false,
         		roleType : "section"
        	},
         	
         	"banner" : {
         		reqName : false,
         		container : null,
         		props : ["aria-expanded"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: false,
         		roleType : "landmark"
         	},
         	
         	"button" : {
         		reqName : true,
         		container : null,
         		props : ["aria-expanded", "aria-pressed"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : "input[@type='button']",
         		nameFromContent: true,
         		supportOnClick: true,
         		roleType : "widget"
         	},
         	
         	"checkbox" : {
         		reqName : true,
         		container : null,
         		props : null,
         		reqProps : ["aria-checked"],
         		reqChildren : null,
         		htmlEquiv : "input[@type='checkbox']",
         		nameFromContent: true,
         		supportOnClick: true,
         		roleType : "widget"
         	},
         	
             "columnheader" : {
         		reqName : true,
         		container : ["row"],
         		props : ["aria-expanded", "aria-sort", "aria-readonly", "aria-selected", "aria-required"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : "th",
         		nameFromContent: true,
         		roleType : "section"
         	},
         	
         	"combobox" : {
         		reqName : true,
         		container : null,
         		props : ["aria-autocomplete", "aria-required", "aria-activedescendant"],
         		reqProps : ["aria-expanded"],
         		reqChildren : ["listbox", "textbox"],
         		htmlEquiv : "select",
         		nameFromContent: false,
         		roleType : "widget"
         	},
         	
         	"complementary" : {
         		reqName : false,
         		container : null,
         		props : ["aria-expanded"],
         		reqProps : ["aria-labelledby"],
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: false,
         		roleType : "landmark"
         	},
         	
         	"contentinfo" : {
         		reqName : false,
         		container : null,
         		props : ["aria-expanded"],
         		reqProps : ["aria-labelledby"],
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: false,
         		roleType : "landmark"
         	},
         	
         	"definition" : {
         		reqName : false,
         		container : null,
         		props : ["aria-expanded"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: false,
         		roleType : "section"
         	},
         	
         	"dialog" : {
         		reqName : true,
         		container : null,
         		props : ["aria-expanded"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: false,
         		roleType : "widget"
         	},
         	
         	"directory" : {
         		reqName : false,
         		container : null,
         		props : ["aria-expanded"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: true,
         		roleType : "section"
         	},
         	
         	"document" : {
         		reqName : true,
         		container : null,
         		props : ["aria-expanded"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : null,
          		nameFromContent: false,
         		roleType : "section"
        	},
         	
         	"form" : {
         		reqName : false,
         		container : null,
         		props : ["aria-expanded"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : "form",
         		nameFromContent: false,
         		roleType : "landmark"
         	},	
         	
         	"grid" : {
         		reqName : true,
         		container : null,
         		props : ["aria-level", "aria-multiselectable", "aria-readonly", "aria-activedescendant", "aria-expanded"],
         		reqProps : null,
         		reqChildren : ["row", "rowgroup"],
         		htmlEquiv : null,
         		nameFromContent: false,
         		roleType : "widget"
         	},
         	
         	"gridcell" : {
         		reqName : true,
         		container : ["row"],
         		props : ["aria-readonly", "aria-selected", "aria-expanded", "aria-required"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: true,
         		roleType : "widget"         		
         	},
         	
         	"group" : {
         		reqName : false,
         		container : null,
         		props : ["aria-activedescendant", "aria-expanded"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : "fieldset",
         		nameFromContent: false,
         		roleType : "section"         		
         	},
         	
         	"heading" : {
         		reqName : true,
         		container : null,
         		props : ["aria-level", "aria-expanded"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : "h1 | h2 | h3 | h4 | h5 |h6",
         		nameFromContent: true,
         		roleType : "section"         		
         	},
         	
         	"img" : {
         		reqName : true,
         		container : null,
         		props : ["aria-expanded"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : "img",
         		nameFromContent: false,
         		roleType : "section"         		
         	},
         	
         	"link" : {
         		reqName : true,
         		container : null,
         		props : null,
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : "a",
         		nameFromContent: true,
         		supportOnClick: true,
         		roleType : "widget"
         	},
         	
         	"list" : {
         		reqName : false,
         		container : null,
         		props : ["aria-expanded"],
         		reqProps : null,
         		reqChildren : ["group", "listitem"],
         		htmlEquiv : "ul | ol",
         		nameFromContent: false,
         		roleType : "section"
         	},
         	
         	"listbox" : {
         		reqName : true,
         		container : null,
         		props : ["aria-expanded", "aria-activedescendant", "aria-multiselectable", "aria-required"],
         		reqProps : null,
         		reqChildren : ["option"],
         		htmlEquiv : "select",
         		nameFromContent: false,
         		supportOnClick: true,
         		roleType : "widget"
         	},
         	
         	"listitem" : {
         		reqName : true,
         		container : ["list"],
         		props : ["aria-expanded", "aria-level", "aria-posinset", "aria-setsize"],
         		reqProps : null,
         		reqChildren : null,
         		nameFromContent: true,
         		htmlEquiv : "section",
         		supportOnClick: true,
         		roleType : "widget"
         	},
         	
         	"log" : {
         		reqName : false,
         		container : null,
         		props : ["aria-expanded"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: false,
         		roleType : "live"
         	},
         	
         	"main" : {
         		reqName : false,
         		container : null,
         		props : ["aria-expanded"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: false,
         		roleType : "landmark"
         	},
         	
         	"marquee" : {
         		reqName : true,
         		container : null,
         		props : ["aria-expanded"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: false,
         		roleType : "section"
         	},
         	
         	"math" : {
         		reqName : false,
         		container : null,
         		props : ["aria-expanded"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: false,
         		roleType : "widget"
         	},
         	
         	"menu" : {
         		reqName : true,
         		container : null,
         		props : ["aria-expanded", "aria-activedescendant"],
         		reqProps : null,
         		reqChildren : ["menuitem", "menuitemcheckbox", "menuitemradio"],
         		htmlEquiv : null,
         		nameFromContent: false,
         		supportOnClick: true,
         		roleType : "widget"
         	},
         	
         	"menubar" : {
         		reqName : false,
         		container : null,
         		props : ["aria-activedescendant", "aria-expanded"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: false,
         		supportOnClick: true,
         		roleType : "widget"
         	},
         	
         	"menuitem" : {
         		reqName : true,
         		container : ["menu", "menubar"],
         		props : null,
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: true,
         		supportOnClick: true,
         		roleType : "widget"
         	},
         	
         	"menuitemcheckbox" : {
         		reqName : true,
         		container : ["menu", "menubar"],
         		props : null,
         		reqProps : ["aria-checked"],
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: true,
         		supportOnClick: true,
         		roleType : "widget"
         	},
         	
         	"menuitemradio" : {
         		reqName : true,
         		container : ["menu", "menubar"],
         		props : ["aria-selected", "aria-posinset", "aria-setsize"],
         		reqProps : ["aria-checked"],
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: true,
         		supportOnClick: true,
         		roleType : "widget"
         	},
         	
         	"navigation" : {
         		reqName : false,
         		container : null,
         		props : ["aria-expanded"],
         		reqProps : ["aria-labelledby"],
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: false,
         		roleType : "landmark"
         	},
         	
         	"note" : {
         		reqName : false,
         		container : null,
         		props : ["aria-expanded"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: false,
         		roleType : "section"
         	},
         	
         	"option" : {
         		reqName : true,
         		container : ["listbox"],
         		props : ["aria-expanded", "aria-checked", "aria-selected", "aria-posinset", "aria-setsize"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: true,
         		supportOnClick: true,
         		roleType : "widget"
         	},
         	
         	"presentation" : {
         		reqName : false,
         		container : null,
         		props : null,
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: false,
         		roleType : "section"
         	},
         	
         	"progressbar" : {
         		reqName : true,
         		container : null,
         		props : ["aria-valuetext", "aria-valuenow", "aria-valuemax", "aria-valuemin"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: false,
         		roleType : "widget",
         		hasRange: true
         	},
         	
         	"radio" : {
         		reqName : true,
         		container : null,
         		props : ["aria-selected", "aria-posinset", "aria-setsize"],
         		reqProps : ["aria-checked"],
         		reqChildren : null,
         		htmlEquiv : "input[@type='radio']",
         		nameFromContent: true,
         		supportOnClick: true,
         		roleType : "widget"
         	},
         	
         	"radiogroup" : {
         		reqName : true,
         		container : null,
         		props : ["aria-activedescendant", "aria-expanded", "aria-required"],
         		reqProps : ["aria-labelledby"],
         		reqChildren : ["radio"],
         		htmlEquiv : null,
         		nameFromContent: false,
         		supportOnClick: true,
         		roleType : "widget"
         	},
         	
         	"region" : {
         		reqName : false,
         		container : null,
         		props : ["aria-expanded"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : "frame",
         		nameFromContent: false,
         		roleType : "landmark"
         	},
         	
         	"row" : {
         		reqName : false,
         		container : ["grid", "treegrid", "rowgroup"],
         		props : ["aria-level", "aria-selected", "aria-activedescendant", "aria-expanded"],
         		reqProps : null,
         		reqChildren : ["gridcell", "rowheader", "columnheader"],
         		htmlEquiv : null,
         		nameFromContent: true,
         		roleType : "section"
         	},
         	
         	"rowgroup" : {
         		reqName : false,
         		container : ["grid"],
         		props : ["aria-expanded", "aria-activedescendant"],
         		reqProps : null,
         		reqChildren : ["row"],
         		htmlEquiv : null,
         		nameFromContent: true,
         		roleType : "section"
         	},
         	
         	"rowheader" : {
         		reqName : true,
         		container : ["row"],
         		props : ["aria-expanded", "aria-sort", "aria-required", "aria-readonly", "aria-selected"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : "th",
         		nameFromContent: true,
         		roleType : "section"
         	},
         	
         	"scrollbar" : {
         		reqName : true,
         		container : null,
         		props : ["aria-valuetext"],
         		reqProps : ["aria-controls", "aria-orientation", "aria-valuenow", "aria-valuemax", "aria-valuemin"],
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: false,
         		roleType : "widget",
         		hasRange: true
         	},
         	
         	"search" : {
         		reqName : false,
         		container : null,
         		props : ["aria-expanded"],
         		reqProps : ["aria-labelledby"],
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: false,
         		roleType : "landmark"
         	},
         	
         	"separator" : {
         		reqName : false,
         		container : null,
         		props : ["aria-expanded", "aria-orientation"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: false,
         		roleType : "section"
         	},
         	
         	"slider" : {
         		reqName : true,
         		container : null,
         		props : ["aria-orientation", "aria-valuetext"],
         		reqProps : ["aria-valuemax", "aria-valuenow", "aria-valuemin"],
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: false,
         		roleType : "widget",
         		hasRange: true
         	},
         	
         	"spinbutton" : {
         		reqName : true,
         		container : null,
         		props : ["aria-required", "aria-valuetext"],
         		reqProps : ["aria-valuemax", "aria-valuenow", "aria-valuemin"],
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: false,
         		roleType : "widget",
         		hasRange: true
         	},
         	
         	"status" : {
         		reqName : false,
         		container : null,
         		props : ["aria-expanded"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: false,
         		roleType : "live"
         	},
         	
         	"tab" : {
         		reqName : true, // bug in authoring spec??
         		container : ["tablist"],
         		props : ["aria-selected", "aria-expanded"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: true,
         		roleType : "widget"
         	},
         	
         	"tablist" : {
         		reqName : false,
         		container : null,
         		props : ["aria-activedescendant", "aria-expanded", "aria-level"],
         		reqProps : null,
         		reqChildren : ["tab"],
         		htmlEquiv : null,
         		nameFromContent: false,
         		roleType : "widget"
         	},
         	
         	"tabpanel" : {
         		reqName : true,
         		container : null,
         		props : ["aria-expanded"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: false,
         		roleType : "widget"
         	},
         	
         	"textbox" : {
         		reqName : true,
         		container : null,
         		props : ["aria-activedescendant", "aria-autocomplete", "aria-multiline", "aria-readonly", "aria-required"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : "input[@type='text'] | textarea",
         		nameFromContent: false,
         		roleType : "widget"
         	},
         	
         	"timer" : {
         		reqName : true,
         		container : null,
         		props : ["aria-expanded"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: false,
         		roleType : "live"
         	},
         	
         	"toolbar" : {
         		reqName : false,
         		container : null,
         		props : ["aria-activedescendant", "aria-expanded"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: false,
         		supportOnClick: true,
         		roleType : "widget"
         	},
         	
         	"tooltip" : {
         		reqName : true,
         		container : null,
         		props : ["aria-expanded"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: false,
         		roleType : "widget"
         	},
         	
         	"tree" : {
         		reqName : true,
         		container : null,
         		props : ["aria-multiselectable", "aria-activedescendant", "aria-expanded", "aria-required"],
         		reqProps : null,
         		reqChildren : ["group", "treeitem"],
         		htmlEquiv : null,
         		nameFromContent: false,
         		roleType : "widget"
         	},
         	
         	"treegrid" : {
         		reqName : true,
         		container : null,
         		props : ["aria-activedescendant", "aria-expanded", "aria-level", "aria-multiselectable", "aria-readonly", "aria-required"],
         		reqProps : null,
         		reqChildren : ["row"],
         		htmlEquiv : null,
         		nameFromContent: false,
         		roleType : "section"
         	},
         	
         	"treeitem" : {
         		reqName : true,
         		container : ["group", "tree"],
         		props : ["aria-checked", "aria-selected", "aria-expanded", "aria-level", "aria-posinset", "aria-setsize"],
         		reqProps : null,
         		reqChildren : null,
         		htmlEquiv : null,
         		nameFromContent: true,
         		supportOnClick: true,
         		roleType : "widget"
            }
         	
        }, // end designPatterns
        
        getRoleObject : function(role) {

          var dp = this.designPatterns;

          for (var r in dp) {
          
            if (role == r)  return dp[r];
          
          }

          var ar = this.abstractRoles;
          var ar_len = ar.length;
          
          for (var i = 0; i < ar_len; i++) {
          
            if (role == r[i])  return 'abstract';
          
          }

          return null;
        }
        
    };	    
    
}
/**
 * Copyright 2011-2013 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* ---------------------------------------------------------------- */
/*        Utilities and String Extensions                           */
/* ---------------------------------------------------------------- */

/** 
 * @namespace OpenAjax.a11y.util
 */

OpenAjax.a11y.util = OpenAjax.a11y.util || {};

/**
 * @function escapeForJSON
 *
 * @memberOf OpenAjax.a11y.util
 *
 * @desc Returns an safe string for JSON output that has single quotes and new line characters
 *
 * @param  {String}  str - string to escape 
 *
 * @return {String}  Escaped string
 */
 
OpenAjax.a11y.util.escapeForJSON = function(str) {

  if (typeof str === 'string' && str.length > 0) {
    var return_str = str.replace("'", "\\'", "gi");
    return_str = str.replace('"', "'", "gi");
    return_str = return_str.replace("\n", "\\n", "gi");
    return return_str;
  }
  return str;  
};

/**
 * @function cleanForUTF8
 *
 * @memberOf OpenAjax.a11y.util
 *
 * @desc Returns an string with only UTF8 characters
 *
 * @param  {String}  str - string to test
 *
 * @return {String}  String with only UTF 8 
 */
 
OpenAjax.a11y.util.cleanForUTF8 = function(str) {

  var nstr = "";
  var str_len = str.length;
  
  for (var i = 0; i < str_len; i++) {
    c = str[i];
    if (c >= ' ' && c < '~') nstr += c;
  }    
  
  return nstr;  
};


/**
 * @function removeEscapesFromJSON
 *
 * @memberOf OpenAjax.a11y.util
 *
 * @desc Returns an unescaped string from a JSON string that has been escaped for single quotes and new line characters
 *
 * @param  {String}  str - string to un escape 
 *
 * @return {String}  String with escape characters removed
 */
 
OpenAjax.a11y.util.removeEscapesFromJSON = function(str) {

  if (typeof str === 'string' && str.length > 0) {
    var return_str = str.replace("\\'", "'", "gi");
    return_str = return_str.replace("\\n", "\n", "gi");
    return return_str;
  }
  return str;  
};

/**
 * @function getFormattedDate
 *
 * @memberOf OpenAjax.a11y.util
 *
 * @desc Returns a fomratted string (YYYY-MM-DD) represeting the current date
 *       with leading zeros
 *
 * @return {String}  Formatted date string
 */
 
OpenAjax.a11y.util.getFormattedDate = function(str) {

  function leadingZero(n) {
    var n1 = n.toString();
    if (n < 10) n1 = "0" + n;
    return n1;
  }

  var date = new Date();
  
  var y = date.getFullYear();
  var m = date.getMonth() + 1;
  var d = date.getDate();
  var hours = date.getHours() + 1;
  var minutes = date.getMinutes() + 1;

  return y + "-" + leadingZero(m) + "-" + leadingZero(d) + ":" + leadingZero(hours)+ ":" + leadingZero(minutes);

};


/**
 * @function getStringUsingURL
 *
 * @memberOf OpenAjax.a11y.util
 *
 * @desc Reads a URL into a string
 *       Used with creating HTML reports
 *
 * @param  {String}  url     - url to file 
 */
 
OpenAjax.a11y.util.initStringUsingURL = function(url) {

  var xmlhttp = new XMLHttpRequest();

//  OpenAjax.a11y.logger.debug( "REQUESTING URL: " + url);

  xmlhttp.open("GET", url, false);
  xmlhttp.send(null); 
  
  var str = xmlhttp.responseText;
  
//  OpenAjax.a11y.logger.debug( "TEXT: " + str);
  
  return str;
  
};
 


/**
 * @function validLanguageCode
 *
 * @memberOf OpenAjax.a11y.util
 *
 * @desc Identifies if a language code is valid
 *
 * @param  {String}  language code -  INAN language code
 *
 * @return {Boolean}  If a valid language code return true, otherwsie false
 */
 
OpenAjax.a11y.util.validLanguageCode = function(code) {

  var LANGUAGE_CODES = OpenAjax.a11y.LANGUAGE_CODES;
  
  code = code.toLowerCase();

  if ((typeof code === 'string') || code.length) {
  
    var parts = code.split("-");
  
    if (parts.length > 1) {
      for (var i = 0; i <parts.length; i++) {
        if (LANGUAGE_CODES.subtags.indexOf(parts[i]) < 0) return false;          
      }
      return true;
    }
    else {
      if (LANGUAGE_CODES.subtags.indexOf(code) >= 0) return true;      
      if (LANGUAGE_CODES.tags.indexOf(code) >= 0) return true;      
    }
  }  
  
  return false;
  
};
 

/**
 * @function transformElementMarkup
 *
 * @memberOf OpenAjax.a11y.util
 * 
 * @desc Converts element markup in strings to capitalized text (default) or adds <code> element
 *
 * @param {String}  str  - String to convert element text
 * 
 * @return  String 
 */
 
OpenAjax.a11y.util.transformElementMarkup = function(str) {
 
  var new_str = "";
  
  var transform_option = 1; // default is capitalize
  
  if (OpenAjax.a11y.ELEMENT_FORMATING == "HTML") transform_option = 2; // transform to html
  if (OpenAjax.a11y.ELEMENT_FORMATING == "NONE") transform_option = 3; // just removes @ sign from string
  
  if (str && str.length) {
    var max = str.length; 
    var transform_flag = false;
    
    for (var i = 0; i < max; i++) {
    
      var c = str[i];
    
      if (c == '@') { 
      
        if (transform_option == 2) {
          if (transform_flag) 
            new_str += '</code>';
          else             
            new_str += '<code>';
        }    
      
        transform_flag = !transform_flag;
        continue;
      }  
      
      if (transform_flag && transform_option == 1) 
        new_str += c.toUpperCase();
      else
        new_str += c;
    }
  }
  return new_str;
};







/**
 * @function urlExists
 *
 * @memberOf OpenAjax.a11y.util
 *
 * @desc Determines if a URL exits
 *
 * @param {String} url      - url to test if it exists
 *
 * @return  Number  
 */
 
OpenAjax.a11y.util.urlExists = function (url) {

 if (OpenAjax.a11y.SUPPORTS_URL_TESTING && OpenAjax.a11y.URL_TESTING_ENABLED) {
  try {
  
   var http = new XMLHttpRequest();
   http.open('HEAD', url, false);
   http.send(null);
   if (http.status!==404) {
//     OpenAjax.a11y.logger.debug( "URL: " + url + " (valid)");
     return OpenAjax.a11y.URL_RESULT.VALID;
   }
   else {
//     OpenAjax.a11y.logger.debug( "URL: " + url + " (INVALID)");
     return OpenAjax.a11y.URL_RESULT.INVALID;
   }
  }
  catch (e) {
   return OpenAjax.a11y.URL_RESULT.ERROR;
  }
 }
 else {
  return OpenAjax.a11y.URL_RESULT.NOT_TESTED;
 }
 
}; 

/**
 * @function RGBToHex
 * @memberOf OpenAjax.a11y.util
 * 
 * @desc Converts an RGB color to Hex values
 *
 * @param {String} rgb_color - RGB Color
 * 
 * @return  String 
 */
 
OpenAjax.a11y.util.RGBToHEX = function( rgb_color ) {

 function stringToHex(d) {
  var hex = Number(d).toString(16);
  if (hex.length == 1) {
   hex = "0" + hex;
  }
  return hex;
 }

 var i;
 var length; 

 if (!rgb_color) return "000000";

 var hex = [];
 var color_hex = "000000";
 var components = rgb_color.match(/[\d\.]+/g);
  
 if (components && components.length) {
  length = components.length;
  
  if (length == 3) {
   // RGB value
   for (i=0; i<3; i++) {
    hex.push(stringToHex(components[i]));
   } // end loop
 
   color_hex = hex[0] + hex[1] + hex[2]; 
   // OpenAjax.a11y.logger.debug( rgb_color + " " + color_hex );
   
  }
  else {  
   
   if (length == 4) {
    // RGBA value
    for (i=0; i<3; i++) {
     hex[i] = stringToHex(Math.round(parseFloat(components[i])*parseFloat(components[3])));
    } // end loop  
    color_hex = hex[0] + hex[1] + hex[2]; 
   }
  }
 } 
 
 return color_hex;
};


/** 
 * @namespace String
 */

 /**
 * @function isInteger
 * @memberOf String
 */

// string utilities
	
/**
 * @function normalizeSpace
 * @memberOf String
 */

if (typeof String.normalizeSpace == "undefined") {
 String.prototype.normalizeSpace = function () {
  // Replace repeated spaces, newlines and tabs with a single space
  return this.replace(/^\s*|\s(?=\s)|\s*$/g, "");
 }; // end function normalizeSpace
}


/**
 * @function replaceAll
 * @memberOf String
 */

if (typeof String.replaceAll == "undefined") {
  String.prototype.replaceAll = function(str1, str2, ignore) {
    return this.replace(new RegExp(str1.replace(/([\,\!\\\^\$\{\}\[\]\(\)\.\*\+\?\|\<\>\-\&])/g, function(c){return "\\" + c;}), "g"+(ignore?"i":"")), str2);
  }; 
}/*
 * Copyright 2011-2013 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


/* ---------------------------------------------------------------- */
/*                      AbbreviationsCache                          */ 
/* ---------------------------------------------------------------- */

/**
 * @constructor AbbreviationsCache
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Constructor for abbreviations cache object which contains a list of 
 *    abbreviation items representing the abbreviations defined  
 *    in a document. The item also contains a list of all the 
 *    dom element objects that share the same abbreviation 
 *
 * @param {DOMCache}   dom_cache   - Reference to the DOMCache object 
 * 
 * @property {DOMCache} dom_cache  - Reference to the DOMCache object 
 *         
 * @property {Boolean}  up_to_date - Boolean true if the cache has been creating using the current DOMElements, else false
 *                                   NOTE: This is a common property of all caches and is used when selectively build caches 
 *                                         based on whether a rule needs the cache
 *
 * @property {String}   sort_property   - Property of abbreviation item that the list is sorted on  
 * @property {Boolean}  sort_ascending  - true if list is sorted by ascending values, otherwsie false 
 *
 * @property {Array}   abbreviation_items  - List of abbreviation items 
 * @property {Number}  length              - Number of abbreviation items in list 
 *
 * @property {ResultRuleSummary}  rule_summary_result  - Rule results associated with this cache
 */

OpenAjax.a11y.cache.AbbreviationsCache = function (dom_cache) {

  this.dom_cache  = dom_cache;
  this.up_to_date = false;
  
  this.abbreviation_items = [];
  this.length = 0;
 
  this.sort_property  = 'abbreviation_text';
  this.sort_ascending = true;
  
};

/**
 * @method addAbbreviationItem
 *
 * @memberOf OpenAjax.a11y.cache.AbbreviationsCache
 *
 * @desc Adds a DOM Element object with an abbreviation to the abbreviation item list.
 *       If the abreviation item does not exist the function will create one
 *
 * @param {DOMElement}  dom_element  - dom element to add to a abbreviation list
 */

OpenAjax.a11y.cache.AbbreviationsCache.prototype.addAbbreviationItem = function (dom_element) {

 var abbreviation_item = null;
 var abbreviation_items_len = this.abbreviation_items.length;
 var found = false;
 var node_text = dom_element.getText();

 for (var i=0; i < abbreviation_items_len; i++ ) {
  abbreviation_item = this.abbreviation_items[i];
  
  if (node_text == abbreviation_item.abbreviation_text) {
    
    abbreviation_item.dom_elements.push(dom_element);
    abbreviation_item.count = abbreviation_item.dom_elements.length;
    
    found = true; 
    break;
  }
 } // end loop

 if (!found) {
  abbreviation_item = new OpenAjax.a11y.cache.AbbreviationItem(node_text);
  
  abbreviation_item.dom_elements.push(dom_element);
  abbreviation_item.count = abbreviation_item.dom_elements.length;
  abbreviation_item.cache_id = "abbrev_" + this.length;  
  
  this.abbreviation_items.push(abbreviation_item);
  this.length = this.length + 1;
 }

};

/**
 * @deprecated getAbbreviationItemByCacheId
 *
 * @memberOf OpenAjax.a11y.cache.AbbreviationsCache
 *
 * @desc Returns the abbreviation item with the cache id
 *
 * @param {String}  cache_id  - cache id of the abbreviation item object
 *
 * @return {AbbreviationItem} Returns abbreviation item object if cache id found, otherwise null  
 */

OpenAjax.a11y.cache.AbbreviationsCache.prototype.getAbbreviationItemByCacheId = function (cache_id) {
  return this.getItemByCacheId(cache_id);
};

/**
 * @method getItemByCacheId
 *
 * @memberOf OpenAjax.a11y.cache.AbbreviationsCache
 *
 * @desc Returns the abbreviation item with the cache id
 *
 * @param {String}  cache_id  - cache id of the abbreviation item object
 *
 * @return {AbbreviationItem} Returns abbreviation item object if cache id found, otherwise null  
 */

OpenAjax.a11y.cache.AbbreviationsCache.prototype.getItemByCacheId = function (cache_id) {

  var i, j;
  var ai, de;
  var dom_elements, dom_elements_len;
  
  var abbreviation_items     = this.abbreviation_items;
  var abbreviation_items_len = abbreviation_items.length;

  if (cache_id && cache_id.length) {  
  
    for (i = 0; i < abbreviation_items_len; i++) {
      ai = abbreviation_items[i];
    
      if (ai.cache_id == cache_id) return ai;
      
      dom_elements     = ai.dom_elements;
      dom_elements_len = dom_elements.length;
      
      for (j = 0; j < dom_elements_len; j++ ) {
        de = dom_elements[j];
        if (de.cache_id == cache_id) return de;
      } // end loop
    } // end loop
  } 

 return null;
};

/**
 * @method emptyCache
 *
 * @memberOf OpenAjax.a11y.cache.AbbreviationsCache
 *
 * @desc Empties all the abbreviation items from the cache 
 */

OpenAjax.a11y.cache.AbbreviationsCache.prototype.emptyCache = function () {

 this.abbreviation_items.length = 0;
 this.sort_property = 'abbreviation_text';
 this.sort_ascending = true;
 this.up_to_date = false;

};

/**
 * @method updateCacheItems
 *
 * @memberOf OpenAjax.a11y.cache.AbbreviationsCache
 *
 * @desc Updates the AbbreviationsCache object with information from a DOMElement object
 *    This is used during the creation of the cache and is used by the functions for
 *    either creating the cache all at one time or selectively
 *
 * @param {DOMElement}  dom_element  - DOM Element object to add to the abbreviations cache
 */
 
OpenAjax.a11y.cache.AbbreviationsCache.prototype.updateCacheItems = function (dom_element) {

 if ((dom_element.tag_name == 'abbr') ||
   (dom_element.tag_name == 'acronym')) {

   this.addAbbreviationItem(dom_element);
   
 }
 
};

/**
 * @method traverseDOMElementsForAbbreviations
 *
 * @memberOf OpenAjax.a11y.cache.AbbreviationsCache
 *
 * @desc Traverses the DOMElements to update the abbreviation cache
 */
 
OpenAjax.a11y.cache.AbbreviationsCache.prototype.traverseDOMElementsForAbbreviations = function (dom_element) {

 if (!dom_element) return;

 if (dom_element.type == Node.ELEMENT_NODE) {

  this.updateCacheItems(dom_element);
  
  for (var i = 0; i < dom_element.child_dom_elements.length; i++ ) {
   this.traverseDOMElementsForAbbreviations(dom_element.child_dom_elements[i]);
  } // end loop
  
 }  
  
};

/**
 * @method updateCache
 *
 * @memberOf OpenAjax.a11y.cache.AbbreviationsCache
 *
 * @desc Traverses the DOMElements to update the abbreviation cache
 *    This function is used to update the abbreviation cache 
 *    when needed by a rule, it sets the up to date flag when done
 */
 
OpenAjax.a11y.cache.AbbreviationsCache.prototype.updateCache = function () {
 var i;
 var children = this.dom_cache.element_cache.child_dom_elements;
 var children_len = children.length;
 
 for (i=0; i < children_len; i++) {
  this.traverseDOMElementsForAbbreviations(children[i]);
 }  

 this.up_to_date = true;
 
};

/**
 * @method sortAbbreviationItems
 *
 * @memberOf OpenAjax.a11y.cache.AbbreviationsCache
 *
 * @desc Sorts abbreviations by abbreviation_text property
 *
 * @param {Boolean}  ascending  - true if sort in ascending order; false in descending order
 *
 * @return {Boolean}  Returns true if list was sorted, false if not
 */

OpenAjax.a11y.cache.AbbreviationsCache.prototype.sortAbbreviationItems = function(ascending) {

  var swapped = false;
  var temp = null;
  var i;

  if( !this.abbreviation_items || (this.abbreviation_items.length === 0)) {
    return false;
  } // endif

  this.sort_ascending = ascending;
 
  var abbreviation_items_len = this.abbreviation_items.length;

  if( ascending ) {
    do{
      swapped = false;
      for (i = 1; i < abbreviation_items_len; i++ ) {
        if (this.abbreviation_items[i-1].abbreviation_text > this.abbreviation_items[i].abbreviation_text) {
          // swap the values
          temp = this.abbreviation_items[i-1];
          this.abbreviation_items[i-1] = this.abbreviation_items[i];
          this.abbreviation_items[i] = temp;
          swapped = true;
        } 
      } // end loop
    } while (swapped);
  }
  else {
    do {
      swapped = false;
      for (i = 1; i < abbreviation_items_len; i++ ) {
        if (this.abbreviation_items[i-1].abbreviation_text < this.abbreviation_items[i].abbreviation_text) {
          // swap the values
          temp = this.abbreviation_items[i-1];
          this.abbreviation_items[i-1] = this.abbreviation_items[i];
          this.abbreviation_items[i] = temp;
          swapped = true;
        } 
      } // end loop
    } while (swapped);
  } 

  this.sort_property = 'abbreviation_text';
 
  return true;

}; 

/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.AbbreviationsCache
 *
 * @desc Returns a text string representation of the abbreviation cache object 
 *
 * @return {String} Returns string represention the abbreviations cache object
 */

OpenAjax.a11y.cache.AbbreviationsCache.prototype.toString = function () {

 var str ="\n\nAbbreviation Information\n";

 var list_length = this.abbreviation_items.length;
 
 for (var i=0; i < list_length; i++ ) {
  str += this.abbreviation_items[i].toString();  
 } // end loop

 return str;
};

/* ---------------------------------------------------------------- */
/*                      AbbreviationItem                            */ 
/* ---------------------------------------------------------------- */

/**
 * @constructor AbbreviationItem
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Constructor for abbreviation item object which contains information
 *       about dom elements that share the same abbreviation 
 * 
 * @param  {String}  abbreviation  - text of abbreviation
 *         
 * @property  {String}  cache_id            - String that uniquely identifies the cache element in the DOMCache
 * @property  {String}  abbreviation_text   - text of abbreviation
 *
 * @property  {Array}   dom_elements  - List of dom elements associated with the abbreviation 
 * @property  {Number}  count         - Number of dom elements that share this abbreviation
 */
 
OpenAjax.a11y.cache.AbbreviationItem = function (abbreviation) {

  this.cach_id = "";
  
  this.abbreviation_text = abbreviation;
  
  this.dom_elements = [];
  this.count = 0;
   
}; 

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.AbbreviationItem
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.AbbreviationItem.prototype.getNodeResults = function () {
  return this.dom_elements[0].getNodeResults();
};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.AbbreviationItem
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style display objects
 */

OpenAjax.a11y.cache.AbbreviationItem.prototype.getStyle = function () {

  return this.dom_elements[0].getStyle();
  
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.AbbreviationItem
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of attribute display object
 */

OpenAjax.a11y.cache.AbbreviationItem.prototype.getAttributes = function (unsorted) {

  return [];
  
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.AbbreviationItem
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of cache property display object
 */

OpenAjax.a11y.cache.AbbreviationItem.prototype.getCacheProperties = function (unsorted) {

  return [];
  
};


/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.AbbreviationItem
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.AbbreviationItem.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};

/**

 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.AbbreviationItem
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event item display objects
 */

OpenAjax.a11y.cache.AbbreviationItem.prototype.getEvents = function () {
   
  return [];
  
};



/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.AbbreviationItem
 *
 * @desc Returns a text string representation of the abbreviation item object 
 *
 * @return {String} Returns string represention the abbreviation item object
 */

OpenAjax.a11y.cache.AbbreviationItem.prototype.toString = function () {

 return "Abbreviation: " + abbreviation_text; 
};



/*
 * Copyright 2011-2013 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* ---------------------------------------------------------------- */
/*                       ColorContrstCache                          */ 
/* ---------------------------------------------------------------- */

/**
 * @constructor ColorContrastCache
 *
 * @memberOf OpenAjax.a11y.cache
 *
* @desc Constructor for ColorContrastCache object which contains a list of
*    color contrast items representing the color contrast combinations
*    used in a document. The item also contains a list of all the
*    DOM Element nodes that contain that color contrast combination
 *
 * @param {DOMCache}   dom_cache   - Reference to the DOMCache object 
 * 
 * @property {DOMCache} dom_cache  - Reference to the DOMCache object 
 *         
 * @property {Boolean}  up_to_date - Boolean true if the cache has been creating using the current DOMElements, else false
 *                                   NOTE: This is a common property of all caches and is used when selectively build caches 
 *                                         based on whether a rule needs the cache
 *
 * @property {String}    sort_property   - Property of ColorContrastItem that the list is sorted on  
 * @property {Boolean}   sort_ascending  - true if list is sorted by ascending values, otherwsie false 
 *
 * @property {Array}    color_contrst_items  - List of color contrast items 
 * @property {Number}   length               - Number of color contrast items in list 
 *
 * @property {ResultRuleSummary}  rule_summary_result  - Rule results associated with this cache
 */

OpenAjax.a11y.cache.ColorContrastCache = function (dom_cache) {
  
  this.dom_cache = dom_cache;
  this.color_contrast_items =[];
  
  this.sort_property = 'color_contrast_ratio';
  this.sort_ascending = false;
  
  this.up_to_date = false;
  this.length = 0;
  
  
};

/**
 * @method addColorContrastItem
 *
 * @memberOf OpenAjax.a11y.cache.ColorContrastCache
 *
 * @desc Adds a DOM text object information to a color contrast item in the color contrast
 *       cache, if it does not match any of the current color contrast items it will create a
 *       new color contrast item.
 *
 * @param {DOMText}  dom_text_node  - dom text_node to add to color contrast list
 */

OpenAjax.a11y.cache.ColorContrastCache.prototype.addColorContrastItem = function (dom_text_node) {
  
  var i;
  
  var cci;
  var cs;
  var color_contrast_items_len = this.color_contrast_items.length;
  var found = false;
  
  for (i = 0; i < color_contrast_items_len; i++) {
    cci = this.color_contrast_items[i];
    cs = dom_text_node.computed_style;
    
    // OpenAjax.a11y.logger.debug("color compare " + dom_text_node.computed_style.color + " with " + item.color );
    
    if ( cci && 
         cci.color &&
         (cs.color_hex     == cci.color)         &&
         (cs.is_large_font == cci.is_large_font) &&
         (cs.background_color_hex == cci.background_color) &&
         ((cs.background_image == 'none' && cci.background_image == 'none') ||
          ((cs.background_image    == cci.background_image) &&
           (cs.background_repeat   == cci.background_repeat) &&
           (cs.background_position == cci.background_position)))) {
      
      cci.dom_text_nodes.push(dom_text_node);
      cci.node_count = cci.dom_text_nodes.length;
      
      cci.addToCharacterCount(dom_text_node.text_length);
      
      found = true;
      break;
    }
  }
  // end loop
  
  if (!found) {
    cs = dom_text_node.computed_style;
    
    cci = new OpenAjax.a11y.cache.ColorContrastItem(cs.font_family, cs.font_size, cs.font_weight, cs.color_hex, cs.background_color_hex, cs.background_image, cs.background_repeat, cs.background_position, cs.is_large_font, cs.color_contrast_ratio, dom_text_node.character_count);
    
    cci.dom_text_nodes.push(dom_text_node);
    cci.node_count = cci.dom_text_nodes.length;
    
    this.color_contrast_items.push(cci);
    this.length = this.length + 1;
    cci.cache_id = "cc_" + this.length;
  }
};

/**
 * @method getColorContrastItemById
 *
 * @memberOf OpenAjax.a11y.cache.ColorContrastCache
 *
 * @desc Returns the color contrast item with the cache id
 *
 * @param {String}  cache_id  - cache id of the color contrast item
 *
 * @return {ColorContrastItem} Returns color contrst item if cache id found, otherwise null  
 */

OpenAjax.a11y.cache.ColorContrastCache.prototype.getColorContrastItemById = function (cache_id) {
  this.getItemByCacheId(cache_id);
};

/**
 * @method getItemByCacheId
 *
 * @memberOf OpenAjax.a11y.cache.ColorContrastCache
 *
 * @desc Returns the color contrast item with the cache id
 *
 * @param {String}  cache_id  - cache id of the color contrast item
 *
 * @return {ColorContrastItem} Returns color contrst item if cache id found, otherwise null  
 */

OpenAjax.a11y.cache.ColorContrastCache.prototype.getItemByCacheId = function (cache_id) {
  
  var i, j;
  var cci, dtn;
  var dom_text_nodes, dom_text_nodes_len;

  var color_contrast_items     = this.color_contrast_items;
  var color_contrast_items_len = color_contrast_items.length;
  
  if (cache_id && cache_id.length) {
  
    for (i = 0; i < color_contrast_items_len; i++) {
    
      cci = color_contrast_items[i];
      
      if (this.color_contrast_items[i].cache_id == cache_id) {
        return this.color_contrast_items[i];
      }

      dom_text_nodes     = cci.dom_text_nodes;
      dom_text_nodes_len = dom_text_nodes.length;
      
      for (j = 0; j < dom_text_nodes_len; j++ ) {
        dtn = dom_text_nodes[j];
        if (dtn.cache_id == cache_id) return dtn;
      } // end loop

    } // end loop
  }
  
  return null;  
};


/**
 * @method emptyCache
 *
 * @memberOf OpenAjax.a11y.cache.ColorContrastCache
 *
 * @desc Empties all the color contrast items from the cache 
 */

OpenAjax.a11y.cache.ColorContrastCache.prototype.emptyCache = function () {
  
  this.color_contrast_items.length = 0;
  this.up_to_date = false;
};

/**
 * @method updateCacheItems
 *
 * @memberOf OpenAjax.a11y.cache.ColorContrastCache
 *
 * @desc Updates the ColorContrastCache object with information from a DOMElement object
 *    This is used during the creation of the cache and is used by the functions for
 *    either creating the cache all at one time or selectively
 *
 * @param {DOMText}  dom_text  - DOM text  object to add to the color contrast cache
 */

OpenAjax.a11y.cache.ColorContrastCache.prototype.updateCacheItems = function (dom_text_node) {

  var tn;

  if (dom_text_node.parent_element) {
    tn =  dom_text_node.parent_element.tag_name;
    
    if (tn != 'script' && tn != 'object' && tn != 'style') {
      this.addColorContrastItem(dom_text_node);
    }
  }  
    
};

/**
 * @method traverseDOMElementsForColorContrast
 *
 * @memberOf OpenAjax.a11y.cache.ColorContrastCache
 *
 * @desc Traverses the DOMElements to update the color contrast cache
 */
 
OpenAjax.a11y.cache.ColorContrastCache.prototype.traverseDOMElementsForColorContrast = function (dom_element) {
  
  if (! dom_element) return;
  
  if (dom_element.type == Node.ELEMENT_NODE) {
    
    for (var i = 0; i < dom_element.child_dom_elements.length; i++) {
      this.traverseDOMElementsForColorContrast(dom_element.child_dom_elements[i]);
    }
    // end loop
  }
  else {
    this.updateCacheItems(dom_element);
  }  
  
};

/**
 * @method updateCache
 *
 * @memberOf OpenAjax.a11y.cache.ColorContrastCache
 *
 * @desc Traverses the DOMElements to update the color contrast cache
 *    This function is used to update the color contrast cache
 *    when needed by a rule, it sets the up to date flag when done
 */

OpenAjax.a11y.cache.ColorContrastCache.prototype.updateCache = function () {
  var i;
  var children = this.dom_cache.element_cache.child_dom_elements;
  var children_len = children.length;
  
  for (i = 0; i < children_len; i++) {
    this.traverseDOMElementsForColorContrast(children[i]);
  }
  
  this.up_to_date = true;
};

/**
 * @method sortCCRItems
 *
 * @memberOf OpenAjax.a11y.cache.ColorContrastCache
 *
 * @desc Sorts abbreviations by color contrast cache by color contrast ratio property
 *
 * @param {Boolean}  ascending  - true if sort in ascending order; false in descending order
 *
 * @return {Boolean}  Returns true if list was sorted, false if not
 */

OpenAjax.a11y.cache.ColorContrastCache.prototype.sortCCRItems = function (ascending) {
  
  var swapped = false;
  var temp = null;
  var i;
  
  if (! this.color_contrast_items || (this.color_contrast_items.length === 0)) {
    return false;
  }
  // endif
  
  this.sort_ascending = ascending;
  
  var color_contrast_items_len = this.color_contrast_items.length;
  
  if (ascending) {
    do {
      swapped = false;
      for (i = 1; i < color_contrast_items_len; i++) {
        if (parseInt(this.color_contrast_items[i - 1].color_contrast_ratio, 10) > parseInt(this.color_contrast_items[i].color_contrast_ratio, 10)) {
          // swap the values
          temp = this.color_contrast_items[i - 1];
          this.color_contrast_items[i - 1] = this.color_contrast_items[i];
          this.color_contrast_items[i] = temp;
          swapped = true;
        }
      }
      // end loop
    }
    while (swapped);
  } else {
    do {
      swapped = false;
      for (i = 1; i < color_contrast_items_len; i++) {
        if (parseInt(this.color_contrast_items[i - 1].color_contrast_ratio, 10) < parseInt(this.color_contrast_items[i].color_contrast_ratio, 10)) {
          // swap the values
          temp = this.color_contrast_items[i - 1];
          this.color_contrast_items[i - 1] = this.color_contrast_items[i];
          this.color_contrast_items[i] = temp;
          swapped = true;
        }
      }
      // end loop
    }
    while (swapped);
  }
  
  this.sort_property = 'color_contrast_ratio';
  
  return true;
};

/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.ColorContrastCache
 *
 * @desc Returns a text string representation of the color contrast cache object 
 *
 * @return {String} Returns string represention the color contrast cache object
 */

OpenAjax.a11y.cache.ColorContrastCache.prototype.toString = function () {
  
  var i;
  
  var item;
  
  var str = "\n\nColor Contrast List Information\n";
  
  var list_length = this.color_contrast_items.length;
  
  for (i = 0; i < list_length; i++) {
    str += this.color_contrast_items[i].toString();
  }
  // end loop
  
  return str;
};

/* ---------------------------------------------------------------- */
/*                      ColorContrastItem                           */ 
/* ---------------------------------------------------------------- */

/**
 * @constructor ColorContrastItem
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Constructor for ColorContrastItem object which contains information
 *       about dom elements that share the same abbreviation 
 * 
 * @param  {String}  font_family           - Value of the CSS font family property
 * @param  {String}  font_size             - Value of the CSS font size property
 * @param  {String}  font_weight           - Value of the CSS font weight property
 * @param  {String}  color                 - Value of the CSS font color property
 * @param  {String}  background_color      - Value of the CSS background color property
 * @param  {String}  background_image      - Value of the CSS background image property
 * @param  {String}  background_repeat     - Value of the CSS background repeat property
 * @param  {String}  background_position   - Value of the CSS background position property
 * @param  {String}  color_contrast_ratio  - Calculated color contrast ratio
 * @param  {String}  count                 - Initial number of characters
 *         
 * @property  {String}  cache_id  - String that uniquely identifies the cache element in the DOMCache
 *
 * @property  {String}  font_family           - Value of the CSS font family property
 * @property  {String}  font_size             - Value of the CSS font size property
 * @property  {String}  font_weight           - Value of the CSS font weight property
 * @property  {String}  color                 - Value of the CSS font color property
 * @property  {String}  background_color      - Value of the CSS background color property
 * @property  {String}  background_image      - Value of the CSS background image property
 * @property  {String}  background_repeat     - Value of the CSS background repeat property
 * @property  {String}  background_position   - Value of the CSS background position property
 * @property  {String}  color_contrast_ratio  - Calculated color contrast ratio
 * @property  {String}  character_count       - Number of characters in the document that share these color contrast properties
 *  
 * @property  {Boolean}  is_large_font  - true if font is considered large, otherwise false 
 *  
 * @property  {String}   dom_elements - List of dom elements with the same color contrast item properties
 */

OpenAjax.a11y.cache.ColorContrastItem = function (font_family, font_size, font_weight, color, bg_color, bg_image, bg_repeat, bg_position, is_large_font, ccr, count) {

  this.cache_id = "";

  this.font_family          = font_family;
  this.font_size            = font_size;
  this.font_weight          = font_weight;
  this.color                = color;
  this.background_color     = bg_color;
  this.background_image     = bg_image;
  this.background_repeat    = bg_repeat;
  this.background_position  = bg_position;
  
  this.color_contrast_ratio = ccr;
  this.character_count = count;
  
  this.is_large_font = is_large_font;
  
  this.dom_text_nodes = [];
};

/**
 * @member addToCharacterCount
 *
 * @memberOf OpenAjax.a11y.cache.ColorContrastItem
 *
 * @desc Add to the total number of characters in the document that matches
 *       the properties of this color contrast item
 */

OpenAjax.a11y.cache.ColorContrastItem.prototype.addToCharacterCount = function (length) {
  
  this.character_count += length;
};

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.ColorContrastItem
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.ColorContrastItem.prototype.getNodeResults = function () {
  return this.dom_text_nodes[0].getNodeResults();
};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.ColorContrastItem
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style display objects
 */

OpenAjax.a11y.cache.ColorContrastItem.prototype.getStyle = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  var properties = [];
  
  cache_nls.addPropertyIfDefined(properties, this, 'is_large_font');
  cache_nls.addPropertyIfDefined(properties, this, 'color_contrast_ratio');
 
  cache_nls.addPropertyIfDefined(properties, this, 'color');
  cache_nls.addPropertyIfDefined(properties, this, 'background_color');
  cache_nls.addPropertyIfDefined(properties, this, 'background_image');
  cache_nls.addPropertyIfDefined(properties, this, 'background_repeat');
  cache_nls.addPropertyIfDefined(properties, this, 'background_position');

  cache_nls.addPropertyIfDefined(properties, this, 'font_family');
  cache_nls.addPropertyIfDefined(properties, this, 'font_size');
  cache_nls.addPropertyIfDefined(properties, this, 'font_weight');  
  
  return properties;
  
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.ColorContrastItem
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of attribute display object
 */

OpenAjax.a11y.cache.ColorContrastItem.prototype.getAttributes = function (unsorted) {

  return [];
  
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.ColorContrastItem
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of cache property display object
 */

OpenAjax.a11y.cache.ColorContrastItem.prototype.getCacheProperties = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  var properties = [];
  
  cache_nls.addPropertyIfDefined(properties, this, 'character_count');
  
  return properties;
  
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.ColorContrastItem
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.ColorContrastItem.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return null;
  }
  
  return this[property];
};

/**
 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.ColorContrastItem
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event item display objects
 */

OpenAjax.a11y.cache.ColorContrastItem.prototype.getEvents = function () {
   
  return [];
  
};


/**
 * @member toString
 *
 * @memberOf OpenAjax.a11y.cache.ColorContrastItem
 *
 * @desc Returns a text string representation of the color contrast item object 
 *
 * @return {String} Returns string represention the color contrast item object
 */

OpenAjax.a11y.cache.ColorContrastItem.prototype.toString = function () {
  
  var str = this.dom_text_nodes.length;
  
  if (this.dom_text_nodes.length != 1) str += " elements"; 
  else str += " element";
    
  return str;
};
/*
 * Copyright 2011, 2012 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* ---------------------------------------------------------------- */
/*                       ControlInfo                                */ 
/* ---------------------------------------------------------------- */

/**
 * @constructor ControlInfo
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates a ControlInfo object for preserving the current control information 
 *        when traversing the DOM for form control information
 *
 * @param {ControlInfo} control_info - Current ControlInfo object
 *
 * @property {ControlElement}   control_element  - Parent ControlElement (if any)
 * @property {FieldsetElement}  fieldset_element - Parent FieldsetElement (if any)
 * @property {SelectElement}    select_element   - Parent SelectElement (if any)
 * @property {LabelElement}     label_element    - Parent LabelElement (if any)
 * @property {FormElement}      form_element     - Parent FormElement (if any)
 */

OpenAjax.a11y.cache.ControlInfo = function (control_info) {
 
 if (control_info) {
  this.control_element  = control_info.control_element;
  this.fieldset_element = control_info.fieldset_element;
  this.select_element   = control_info.select_element;
  this.label_element    = control_info.label_element; 
  this.form_element     = control_info.form_element; 
  this.parent_widget    = control_info.parent_widget; 
 }
 else {
  this.control_element  = null;
  this.fieldset_element = null;
  this.select_element   = null;
  this.label_element    = null;
  this.form_element     = null;
  this.parent_widget    = null; 
 } 
}; 

/* ---------------------------------------------------------------- */
/*                       ControlsCache                              */ 
/* ---------------------------------------------------------------- */

/** 
 * @constructor ControlsCache
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc ControlsCache is the constructor for lists of control cache element objects and
 *       the root of a tree representation of the control cache element relationships
 *       
 * @param {DOMCache}   dom_cache   - Reference to the DOMCache object 
 * 
 * @property {DOMCache} dom_cache  - Reference to the DOMCache object 
 *         
 * @property {Boolean}  up_to_date - Boolean true if the cache has been creating using the current DOMElements, else false
 *                                   NOTE: This is a common property of all caches and is used when selectively build caches 
 *                                          based on whether a rule needs the cache
 *
 * @property {Array}    child_cache_elements  - Root array of the tree representation of the controls in the document 
 *
 * @property {Array}    control_elements      - List of all the InputElement, TextareaElement, ButtonElement, SelectElement, 
 *                                              OptionElements and OptgroupElement objects in the cache
 *
 * @property {Number}   control_length        - Length of the control_elements array and used in calculating cache IDs
 *
 * @property {Array}    label_elements        - List of all the LabelElement objects in the cache
 * @property {Number}   label_length          - Length of the label_elements array and used in calculating cache IDs
 *
 * @property {Array}    fieldset_elements     - List of all the FieldsetElement objects in the cache
 * @property {Number}   fieldset_length       - Length of the Fireldset_elements array and used in calculating cache IDs
 *
 * @property {Array}    form_elements         - List of all the FormElement objects in the cache
 * @property {Number}   form_length           - Length of the form_elements array and used in calculating cache IDs
 *
 * @property {String}   sort_property         - The property the list of control element object is currenlty sorted by
 * @property {Boolean}  ascending             - true if the list is ascending order or false if descending
 *
 * @property {ResultRuleSummary}  rule_summary_result  - Rule results associated with this
 */

OpenAjax.a11y.cache.ControlsCache = function (dom_cache) {

  this.dom_cache     = dom_cache;
  this.up_to_date    = false;
 
  this.child_cache_elements  = [];
 
  this.control_elements = [];
  this.control_length  = 0;

  this.widget_elements = [];
  this.widget_length  = 0;
  
  this.elements_with_role = [];
  this.elements_with_aria_attributes = [];

  this.elements_with_events = [];

  this.label_elements  = [];
  this.label_length   = 0;
  
  this.fieldset_elements = [];
  this.fieldset_length = 0;
  
  this.form_elements   = [];
  this.form_length   = 0;

  this.sort_property  = 'document_order';
  this.ascending    = true;
 
};

/**
 * @method addChildControl
 * 
 * @memberOf OpenAjax.a11y.cache.ControlsCache
 * 
 * @desc Adds a cache control element to the root tree representation of control elements
 *
 * @param  {WidgetElement | ButtonElement | FieldsetElement | FormElement | InputElement | LabelElement| LegendElement | OptgroupElement | OptionElement | SelectElement | TextareaElement } control_element   - Cache control element object to add 
 */

OpenAjax.a11y.cache.ControlsCache.prototype.addChildControl = function (control_element) {

  if (control_element) {
    this.child_cache_elements.push(control_element); 
  }  
   
}; 

/** 
 * @method addControlElement
 *
 * @memberOf OpenAjax.a11y.cache.ControlsCache
 *
 * @desc Adds a cache control element to the list of controls array and generates a cache_id for each control 
 *
 * @param  {WidgetElement | ButtonElement | FieldsetElement | FormElement | InputElement | LabelElement| LegendElement | OptgroupElement | OptionElement | SelectElement | TextareaElement } control_element   - Cache control element object to add 
 *
 * @return  {Number} Returns the number of control objects in the control_elements array
 */

OpenAjax.a11y.cache.ControlsCache.prototype.addControlElement = function (control_element) {

//  OpenAjax.a11y.logger.debug("  Adding control element: " + control_element.dom_element.tag_name + " ("+ control_element.control_type + ")");

  // item must exist and have the position property
  if (control_element) {
    this.control_length += 1;
    control_element.document_order = this.control_length;
    control_element.cache_id = "control_" + this.control_length;
    this.control_elements.push( control_element );
    return true;
  } 

  return this.control_length;

};

/**
 * @method addLabelElement
 *
 * @memberOf OpenAjax.a11y.cache.ControlsCache
 *
 * @desc Add LabelElement object to list of label elements and generates a cache id for the object
 *
 * @param  {LabelElement} label_element  - LabelElement object to add 
 *
 * @return  Nothing 
 */

OpenAjax.a11y.cache.ControlsCache.prototype.addLabelElement = function (label_element) {

  if (label_element) {
    this.label_length += 1;
    label_element.document_order = this.label_length;
    label_element.cache_id = "label_" + this.label_length;
    this.label_elements.push( label_element );
  } 

};

/**
 * @method addFormElement
 *
 * @memberOf OpenAjax.a11y.cache.ControlsCache
 *
 * @desc Add a FormElement object to the list of form elements and generates a cache id for the object 
 *
 * @param  {FormElement} form_element  - FormElement to add 
 *
 * @return {Number} Returns number of FormElement objects in the list of form elements
 */

OpenAjax.a11y.cache.ControlsCache.prototype.addFormElement = function (form_element) {

  // item must exist and have the position property
  if (form_element) {
    this.form_length = this.form_length + 1;
    form_element.document_order = this.form_length;
    form_element.cache_id = "form_" + this.form_length;
    this.form_elements.push( form_element );
  } 

  return this.form_length;

};

/** 
 * @method addFieldsetElement
 *
 * @memberOf OpenAjax.a11y.cache.ControlsCache
 *
 * @desc Adds a FieldsetElement to the list of fieldset elements and generates a cache id for the object 
 *
 * @param  {FieldsetElement}  fieldset_element  - FieldsetElement to add 
 *
 * @return {Number} Returns the number of FieldsetElement objects in the list of fieldset elements
 */

OpenAjax.a11y.cache.ControlsCache.prototype.addFieldsetElement = function (fieldset_element) {

  // item must exist and have the position property
  if (fieldset_element) {
    this.fieldset_length = this.fieldset_length + 1;
    fieldset_element.document_order = this.fieldset_length;
    fieldset_element.cache_id = "fieldset_" + this.fieldset_length;
    this.fieldset_elements.push(fieldset_element);
  } 

  return this.fieldset_length;

};

/**
 * @method emptyCache
 *
 * @memberOf OpenAjax.a11y.cache.ControlsCache
 *
 * @desc Resests the ControlsCache object properties and empties all the lists and arrays 
 */

OpenAjax.a11y.cache.ControlsCache.prototype.emptyCache = function () {

  this.up_to_date    = false;
 
  this.child_cache_elements  = [];
 
  this.control_elements = [];
  this.control_length  = 0;
  
  this.label_elements  = [];
  this.label_length   = 0;
  
  this.fieldset_elements = [];
  this.fieldset_length = 0;
  
  this.form_elements   = [];
  this.form_length   = 0;

  this.sort_property  = 'document_order';
  this.ascending    = true;

};

/**
 * @method updateCacheItems
 *
 * @memberOf OpenAjax.a11y.cache.ControlsCache
 *
 * @desc Updates the ControlsCache object by checking to see if a DOMElement
 *          should be added to the control cache objects
 *  
 * @param  {DOMElement}   dom_element   - DOMElement object to check for inclusion in controls cache
 * @param  {ControlInfo}  control_info  - Information about the current control relationships in the DOM
 *
 * @return {ControlInfo}  Returns updated control info object 
 */
 
OpenAjax.a11y.cache.ControlsCache.prototype.updateCacheItems = function (dom_element, control_info) {
 
  var be;
  var fe;
  var ie;
  var le;
  var me;
  var oe;
  var pe;
  var se;
  var te;  
  var we;
  
  var ci = new OpenAjax.a11y.cache.ControlInfo(control_info);

  // check for widget
  
  if (dom_element.hasEvents()) this.elements_with_events.push(dom_element);
  
  if (dom_element.has_role) this.elements_with_role.push(dom_element);
  
  if (dom_element.has_aria_attributes) {

    // OpenAjax.a11y.logger.debug(dom_element + " has aria attributes");
    this.elements_with_aria_attributes.push(dom_element);
  }  
 
  if (dom_element.is_widget) {
    
    we = new OpenAjax.a11y.cache.WidgetElement(dom_element, control_info);
    this.addLabel(we, "", OpenAjax.a11y.SOURCE.NONE);
    
    this.addControlElement(we);
    this.widget_elements.push(we);
    
    if (control_info.control_element) {
      control_info.control_element.addChildControl(we);   
    }
    else {
      this.addChildControl(we);     
    }
  
    ci.control_element = we;
    
    if (!we.has_aria_owns) ci.parent_widget = we;
  
  }
  else {

    switch (dom_element.tag_name) {

    case 'form':
      fe = new OpenAjax.a11y.cache.FormElement(dom_element, control_info);

      this.addFormElement(fe); 

      if (control_info.control_element) {
        control_info.control_element.addChildControl(fe);   
      }
      else {
        this.addChildControl(fe);     
      }
  
      ci.control_element = fe;
      ci.form_element = fe;
  
      break;

    case 'fieldset':
      fe = new OpenAjax.a11y.cache.FieldsetElement(dom_element, control_info);
    
      this.addFieldsetElement(fe); 
  
      if (control_info.control_element) {
        control_info.control_element.addChildControl(fe);   
      }
      else {
        this.addChildControl(fe);     
      }
  
      ci.control_element = fe;
      ci.fieldset_element = fe;
      break;

    case 'legend':
      le = new OpenAjax.a11y.cache.LegendElement(dom_element, control_info);
      le.computed_label = this.getElementTextContent(le, false);
      le.computed_label_for_comparison = le.computed_label.normalizeSpace().toLowerCase();
      le.computed_label_length = le.computed_label_for_comparison.length;

      this.addLabelElement(le); 
  
      if (control_info.control_element) {
        control_info.control_element.addChildControl(le);   
      }
      else {
        this.addChildControl(le);     
      }

      if (control_info.fieldset_element) {
        control_info.fieldset_element.legend_element = le;
      }

      ci.control_element = le;
      break;

    case 'label':
      le = new OpenAjax.a11y.cache.LabelElement(dom_element, control_info);
      le.computed_label = this.getElementTextContent(le, false);
      le.computed_label_for_comparison = le.computed_label.normalizeSpace().toLowerCase();
      le.computed_label_length = le.computed_label_for_comparison.length;
    
      this.addLabelElement(le); 
  
      if (control_info.control_element) {
        OpenAjax.a11y.logger.debug("  Adding child control: " + control_info.control_element);
        control_info.control_element.addChildControl(le);   
      }
      else {
        this.addChildControl(le);     
      }
    
      ci.control_element = le;
      ci.label_element  = le;
      break;

    case 'input':
      ie = new OpenAjax.a11y.cache.InputElement(dom_element, control_info);
      this.addLabel(ie, "", OpenAjax.a11y.SOURCE.NONE);
      
      if (ie.dom_element.node.type.toLowerCase() != "hidden") {
    
        this.addControlElement(ie); 
  
        if (control_info.control_element) {
          control_info.control_element.addChildControl(ie);   
        }
        else {
          this.addChildControl(ie);     
        }
      
        if (control_info.form_element) {
          control_info.form_element.number_of_controls += 1;   
        }
      
        if (control_info.fieldset_element) {
          control_info.fieldset_element.number_of_controls += 1;   
        }
      } 
  
      break;

    case 'button':
      be = new OpenAjax.a11y.cache.ButtonElement(dom_element, control_info);
      this.addLabel(be, "", OpenAjax.a11y.SOURCE.NONE);

      this.addControlElement(be); 

      if (control_info.control_element) {
        control_info.control_element.addChildControl(be);   
      }
      else {
        this.addChildControl(be);     
      }

      if (control_info.form_element) {
        control_info.form_element.number_of_controls += 1;   
      }

      if (control_info.fieldset_element) {
        control_info.fieldset_element.number_of_controls += 1;   
      }
    
      ci.control_element = be;
      break;

    case 'meter':
      me = new OpenAjax.a11y.cache.MeterElement(dom_element, control_info);
      this.addLabel(me, "", OpenAjax.a11y.SOURCE.NONE);

      this.addControlElement(me); 

      if (control_info.control_element) {
        control_info.control_element.addChildControl(me);   
      }
      else {
        this.addChildControl(me);     
      }

      if (control_info.form_element) {
        control_info.form_element.number_of_controls += 1;   
      }

      if (control_info.fieldset_element) {
        control_info.fieldset_element.number_of_controls += 1;   
      }
    
      break;


    case 'progress':
      pe = new OpenAjax.a11y.cache.ProgressElement(dom_element, control_info);
      this.addLabel(pe, "", OpenAjax.a11y.SOURCE.NONE);

      this.addControlElement(pe); 

      if (control_info.control_element) {
        control_info.control_element.addChildControl(pe);   
      }
      else {
        this.addChildControl(pe);     
      }

      if (control_info.form_element) {
        control_info.form_element.number_of_controls += 1;   
      }

      if (control_info.fieldset_element) {
        control_info.fieldset_element.number_of_controls += 1;   
      }
    
      break;

    case 'output':
      oe = new OpenAjax.a11y.cache.OutputElement(dom_element, control_info);
      this.addLabel(oe, "", OpenAjax.a11y.SOURCE.NONE);

      this.addControlElement(oe); 

      if (control_info.control_element) {
        control_info.control_element.addChildControl(oe);   
      }
      else {
        this.addChildControl(oe);     
      }

      if (control_info.form_element) {
        control_info.form_element.number_of_controls += 1;   
      }

      if (control_info.fieldset_element) {
        control_info.fieldset_element.number_of_controls += 1;   
      }
    
      break;


    case 'textarea':
      te = new OpenAjax.a11y.cache.TextareaElement(dom_element, control_info);
      this.addLabel(te, "", OpenAjax.a11y.SOURCE.NONE);

      this.addControlElement(te); 

      if (control_info.control_element) {
        control_info.control_element.addChildControl(te);   
      }
      else {
        this.addChildControl(te);     
      }
    
      if (control_info.form_element) {
        control_info.form_element.number_of_controls += 1;   
      }

      if (control_info.fieldset_element) {
        control_info.fieldset_element.number_of_controls += 1;   
      }
    
      break;

    case 'select':
      se = new OpenAjax.a11y.cache.SelectElement(dom_element, control_info);
      this.addLabel(se, "", OpenAjax.a11y.SOURCE.NONE);
  
      this.addControlElement(se); 
  
      if (control_info.control_element) {
        control_info.control_element.addChildControl(se);   
      }
      else {
        this.addChildControl(se);     
      }
    
      if (control_info.form_element) {
        control_info.form_element.number_of_controls += 1;   
      }
  
      if (control_info.fieldset_element) {
        control_info.fieldset_element.number_of_controls += 1;   
      }
    
      ci.select_element = se;
      ci.control_element = se;
      break;

    case 'optgroup':
      oe = new OpenAjax.a11y.cache.OptgroupElement(dom_element, control_info);
  
      if (dom_element.node.label && dom_element.node.label.length) {
        oe.label = dom_element.node.label;  
        oe.label_length = oe.label.length;
      } 
 
      if (control_info.control_element) {
       control_info.control_element.addChildControl(oe);   
      }
      else {
        this.addChildControl(oe);     
      }
 
      ci.control_element = oe;
      break;

    case 'option':
      oe = new OpenAjax.a11y.cache.OptionElement(dom_element, control_info);
  
      oe.computed_label = this.getElementTextContent(oe, false);
      oe.computed_label_for_comparison = oe.computed_label.normalizeSpace().toLowerCase();
      oe.computed_label_length = oe.computed_label_for_comparison.length;

  
      if (control_info.control_element) {
        control_info.control_element.addChildControl(oe);   
      }
      else {
        this.addChildControl(oe);     
      }

      if (control_info.select_element) {
        control_info.select_element.addOption(oe);   
      }

      break;

    default:
  
      break;

    } // end switch
    
    // if we are in a widget there a few HTML elements with implied roles
    
    if (control_info.parent_widget) {
    
      var implied_role = null;
      var role = control_info.parent_widget.dom_element.role;
      var tag_name = dom_element.tag_name;
    
      if (tag_name === 'tr' && (" grid rowgroup treegrid".indexOf(role) > 0)) {
        implied_role = 'row';        
      }

      if ((tag_name === 'td' || tag_name === 'th') && (" grid rowgroup treegrid".indexOf(role) > 0)) {

        var scope = dom_element.node.getAttribute('scope');

        if (typeof scope === 'string') {
          scope = scope.toLowerCase();
          if (scope === 'col') implied_role = 'columnheader';        
          else if (scope === 'row') implied_role = 'rowheader';   
        }  
      }

      if ((tag_name === 'thead' || tag_name === 'tfoot' || tag_name === 'tbody') && role === 'grid') {
        implied_role = 'rowgroup';              
      }

      if (implied_role && (implied_role.length > 0)) {
      
//        OpenAjax.a11y.logger.debug("  Adding implied role: " + implied_role);
        
        dom_element.setImpliedRole(implied_role);

        we = new OpenAjax.a11y.cache.WidgetElement(dom_element, control_info);
        this.addLabel(we, "", OpenAjax.a11y.SOURCE.NONE);
    
        this.addControlElement(we);
        this.widget_elements.push(we);
    
        if (control_info.control_element) {
          control_info.control_element.addChildControl(we);   
        }
        else {
          this.addChildControl(we);     
        }
        
        ci.control_element = we;
        if (!we.has_aria_owns) ci.parent_widget = we;

      }
  
    }
    
  }   

  return ci;
};

/**
 * @method traverseDOMElementsForControlElements
 *
 * @memberOf OpenAjax.a11y.cache.ControlsCache
 *
 * @desc Traverses DOMElement objects in the tree to update the controls cache 
 *
 * @param  {DOMElement}  dom_element   - DOMElement object to check for inclusion in controls cache
 * @param  {ControlInfo} control_info  - Current control information object that contains information 
 */
 
OpenAjax.a11y.cache.ControlsCache.prototype.traverseDOMElementsForControlElements = function (dom_element, control_info) {
 
  var i;
  var ci;

  if (!dom_element) return;
 
  if (dom_element.type == Node.ELEMENT_NODE) {

    ci = this.updateCacheItems(dom_element, control_info);
  
    for (i = 0; i < dom_element.child_dom_elements.length; i++ ) {
      this.traverseDOMElementsForFormElements(dom_element.child_dom_elements[i], ci);
    } // end loop
  
  }  
}; 

/**
 * @method updateCache
 *
 * @memberOf OpenAjax.a11y.cache.ControlsCache
 *
 * @desc Traverses the DOMElements to update the controls cache
 *       NOTE: This function is only used when the specialized caches
 *       are build as rules need them.  In this condition, if the rules 
 *       dependent on the controls cache are disabled, this cache would 
 *       not be updated
 */
 
OpenAjax.a11y.cache.ControlsCache.prototype.updateCache = function () {
 var i;
 var children = this.dom_cache.element_cache.child_dom_elements;
 var children_len = children.length;
 
 var control_info = new OpenAjax.a11y.cache.ControlInfo(null);
  
 for (i=0; i < children_len; i++) {
  this.traverseDOMElementsForControlElements(children[i], control_info);
 }  
 
 this.calculateControlLabels();
 this.applyAriaOwns();
 
 this.up_to_date = true;
};

/**
 * @method getRuleResults
 *
 * @memberOf OpenAjax.a11y.cache.ControlsCache
 *
 * @desc Returns an array of rule results for the cache items in the controls cache 
 *
 * @param  {Number}  filter  - Filter for returning rules with particular type(s) of   
 *
 * @return {Array} Returns array of rule results, can be empty
 */

OpenAjax.a11y.cache.ControlsCache.prototype.getRuleResults = function (filter) {

  function traverseCacheItems(cache_item) {
  
    var flag = false;
    var de = cache_item.dom_element;
    
    if ((local_filter & RESULT_FILTER.PASS)                  && de.rules_passed.length)        flag = true; 
    if (!flag && (local_filter & RESULT_FILTER.VIOLATION)    && de.rules_violations.length)    flag = true; 
    if (!flag && (local_filter & RESULT_FILTER.WARNING)      && de.rules_warnings.length)      flag = true; 
    if (!flag && (local_filter & RESULT_FILTER.WEBSITE_MANUAL_CHECK) && de.rules_manual_checks.length) flag = true; 
    if (!flag && (local_filter & RESULT_FILTER.PAGE_MANUAL_CHECK)    && de.rules_manual_checks.length) flag = true; 
    if (!flag && (local_filter & RESULT_FILTER.ELEMENT_MANUAL_CHECK) && de.rules_manual_checks.length) flag = true; 
    if (!flag && (local_filter & RESULT_FILTER.NA)       && de.rules_hidden.length)        flag = true; 
    
    if (flag) cache_items.push(cache_item);

    if (cache_item.child_cache_elements) {
      var child_cache_elements     = cache_item.child_cache_elements;
      var child_cache_elements_len = child_cache_elements.length;
  
      for (var i = 0; i < child_cache_elements_len; i++) {
        var ci = child_cache_elements[i];

        traverseCacheItems(ci);
      }
    }  
  }

  var RESULT_FILTER = OpenAjax.a11y.RESULT_FILTER;

  var local_filter;

  if (!filter) 
    local_filter = RESULT_FILTER.ALL;
  else
    local_filter = filter;

  var rule_results = [];
  
  var child_cache_elements     = this.child_cache_elements;
  var child_cache_elements_len = child_cache_elements.length;
  
  for (var i = 0; i < child_cache_elements_len; i++) {
    var ci = child_cache_elements[i];

    traverseCacheItems(ci);
  
  } 

  return cache_items;

};



/**
 * @method getItemByCacheId
 *
 * @memberOf OpenAjax.a11y.cache.ControlsCache
 *
 * @desc Finds the the control cache element object with the matching cache id
 *
 * @param  {String }  cache_id  - Cache id of control cache element object
 *
 * @return {cache control element object} Returns cache control element object if cache id is found, otherwise null
 */

OpenAjax.a11y.cache.ControlsCache.prototype.getItemByCacheId = function (cache_id) {

  var item = null;
  
  item = this.getControlElementByCacheId(cache_id);
  if (item) return item;

  item = this.getLabelElementByCacheId(cache_id);
  if (item) return item;

  item = this.getFormElementByCacheId(cache_id);
  if (item) return item;

  item = this.getFieldsetElementByCacheId(cache_id);
  
  return item;

};

/**
 * @method getControlElementByCacheId
 *
 * @memberOf OpenAjax.a11y.cache.ControlsCache
 *
 * @desc Finds the the control cache element object with the matching cache id
 *
 * @param  {String }  cache_id  - Cache id of control cache element object
 *
 * @return {cache control element object} Returns cache control element object if cache id is found, otherwise null
 */
 
OpenAjax.a11y.cache.ControlsCache.prototype.getControlElementByCacheId = function (cache_id) {

  for (var i = 0; i<this.control_elements.length; i++) {
    if (this.control_elements[i].cache_id == cache_id) return this.control_elements[i];
  }

  return null;
};

/**
 * @method getControlElementById
 *
 * @memberOf OpenAjax.a11y.cache.ControlsCache
 *
 * @desc Finds the the control cache element object with the matching id
 *
 * @param  {String }  id  - id of control cache element object
 *
 * @return {cache control element object} Returns cache control element object if cache id is found, otherwise null
 */
 
OpenAjax.a11y.cache.ControlsCache.prototype.getControlElementById = function (id) {

  for (var i = 0; i < this.control_elements.length; i++) {
    if (this.control_elements[i].dom_element.id == id) {
      return this.control_elements[i];
    }
  }

  return null;
};

/**
 * @method getLabelElementByCacheId
 *
 * @memberOf OpenAjax.a11y.cache.ControlsCache
 *
 * @desc Finds the the LabelElement object with the matching cache id
 *
 * @param  {String}  cache_id  - Cache id of LabelElement object
 *
 * @return {LabelElement}  Returns label element with the cache id if found, otherwise null
 */
 
OpenAjax.a11y.cache.ControlsCache.prototype.getLabelElementByCacheId = function (cache_id) {

  for (var i = 0; i < this.label_elements.length; i++) {
    if (this.label_elements[i].cache_id == cache_id) return this.label_elements[i];
  }

  return null;
};

/**
 * @method getFormElementByCacheId
 *
 * @memberOf OpenAjax.a11y.cache.ControlsCache
 *
 * @desc Finds the the FormElement object with the matching cache id
 *
 * @param  {String}  cache_id  - Cache id of FormElement object
 *
 * @return {FormElement}  Returns form element with the cache id if found, otherwise null
 */
 
OpenAjax.a11y.cache.ControlsCache.prototype.getFormElementByCacheId = function (cache_id) {

 var i;

 for (i=0; i<this.form_elements.length; i++) {
  if (this.form_elements[i].cache_id == cache_id) {
   return this.form_elements[i];
  }
 }

 return null;
};

/**
 * @method getFieldsetElementByCacheId
 *
 * @memberOf OpenAjax.a11y.cache.ControlsCache
 *
 * @desc Finds the the FieldsetElement object with the matching cache id
 *
 * @param  {String}  cache_id  - Cache id of FieldsetElement object
 *
 * @return {FieldsetElement}  Returns fieldset element with the cache id if found, otherwise null
 */
 
OpenAjax.a11y.cache.ControlsCache.prototype.getFieldsetElementByCacheId = function (cache_id) {

 var i;

 for (i=0; i<this.fieldset_elements.length; i++) {
  if (this.fieldset_elements[i].cache_id == cache_id) {
   return this.fieldset_elements[i];
  }
 }

 return null;
};

/**
 * @method getElementTextContent
 *
 * @memberOf OpenAjax.a11y.cache.ControlsCache
 *
 * @desc Traverses the cache to get the text content associated with the label, this will include the 
 *       values of form controls in the label references
 *
 * @param  {LabelElement}  label_element           - LabelElement object to calculate the text content
 * @param  {Boolean}       include_control_values  - True if the values of form controls should be included in 
 *                                                   accessible name calculation
 *
 * @return {String}  Returns the text content of a LabelElement
 */
 
OpenAjax.a11y.cache.ControlsCache.prototype.getElementTextContent = function (label_element, include_control_values) {

 var strings = [];
 
 function getText(dom_element) {
  var i;
  
  // If text node get the text and return
  if( dom_element.type == Node.TEXT_NODE ) {
   var text = dom_element.text;
   strings.push( text );
  } else {
   // if an element for through all the children elements looking for text
   if( dom_element.type == Node.ELEMENT_NODE ) {
   
    switch (dom_element.tag_name) {

    case 'img':
    case 'area':
     strings.push( dom_element.alt );     
     break;
     
    case 'input':
     if (include_control_values && dom_element.node.type == 'text') strings.push(dom_element.node.value);
     break;       

    case 'select':
     // *** need to add some code here to get 
     break;       
     
    default:
     break;    

    } // end switch     
    
    for (i = 0; i < dom_element.child_dom_elements.length; i++ ) {
     getText( dom_element.child_dom_elements[i]);
    }      
    
   }  
  } 
 } // end function getText

 getText(label_element.dom_element); 
 
 return strings.join("").normalizeSpace();
 
};

/**
 * @method calculateLabelsUsingARIA
 *
 * @memberOf OpenAjax.a11y.cache.ControlsCache
 *
 * @desc Interates the array for control cache elements and calculates the accessible name for
 *         any control elements if there is ARIA markup 
 */
 
OpenAjax.a11y.cache.ControlsCache.prototype.calculateLabelsUsingARIA = function () {

  var control_elements     = this.control_elements;
  var control_elements_len = control_elements.length;
  
  // first check if an label by reference
 
  for (var i = 0; i < control_elements_len; i++) {
 
    var ce = control_elements[i];
    var de = ce.dom_element;
    
    if ( (de.aria_labelledby && de.aria_labelledby.length) || 
         (de.aria_label && de.aria_label.length) ||
         (de.role_info)) {
         
      this.dom_cache.getNameFromARIALabel(ce);
      
      // If title attribute is the result clear label for use of other labeling techniques
      if (ce.computed_label_source == OpenAjax.a11y.SOURCE.TITLE_ATTRIBUTE && !ce.role_info) {
        ce.computed_label = "";
        ce.computed_label_for_comparison = "";
        ce.computed_label_length = 0;
        this.addLabel(ce, "", OpenAjax.a11y.SOURCE.NONE);
      }
    }
  }
};


/**
 * @method addFieldsetLegend
 *
 * @memberOf OpenAjax.a11y.cache.ControlsCache
 *
 * @desc Adds legend content to computed label if control is contained in a fieldset/legend
 *
 * @param {Object}  control  -  Control Object
 */
 
OpenAjax.a11y.cache.ControlsCache.prototype.addFieldsetLegend = function (control) {

   // Add fieldset/legend information if defined
   if (control.fieldset_element && 
       control.fieldset_element.legend_element) {
       control.computed_label = control.fieldset_element.legend_element.computed_label + " ";
       control.computed_label_length = control.computed_label.length;
   }
   
};

/**
 * @method addLabel
 *
 * @memberOf OpenAjax.a11y.cache.ControlsCache
 *
 * @desc Adds legend content to computed label if control is contained in a fieldset/legend
 *
 * @param {Object}  control -  Control Object
 * @param {String}  label   -  label text
 * @param {Number}  source  -  label source
 */
 
OpenAjax.a11y.cache.ControlsCache.prototype.addLabel = function (control, label, source) {

  if (source === OpenAjax.a11y.SOURCE.NONE) {
    control.computed_label  = "";
    if (control.dom_element.role_info) control.accessible_name = "";
  } else {
    this.addFieldsetLegend(control);
    control.computed_label += label + " ";
  }
  
  control.computed_label_length = control.computed_label.length;
  control.computed_label_source = source;
  control.computed_label_for_comparison = control.computed_label.normalizeSpace().toLowerCase();
   
};


/**
 * @method calculateLabelsByReference
 *
 * @memberOf OpenAjax.a11y.cache.ControlsCache
 *
 * @desc Iterates the list of label elements and calculates the accessible label for
 *       any control elements that are referenced by label elements with for attribute
 */
 
OpenAjax.a11y.cache.ControlsCache.prototype.calculateLabelsByReference = function () {

  var SOURCE = OpenAjax.a11y.SOURCE;

  var label_elements      = this.label_elements;
  var label_elements_len = label_elements.length;
  
  // first check if an label by reference
 
  for (var i = 0; i < label_elements_len; i++) {
 
    var le = label_elements[i];
 
    var id;
    if (le.for_id) {
      id = le.for_id;
    }
    else {
      id = null;
    }  

    if (id && id.length && !le.hidden_label) {
    
      var ce = this.getControlElementById(id);
      
      if (ce) {

        // check to see if label defined (i.e. an ARIA technique)

        if ((ce.computed_label_source !== SOURCE.ARIA_LABELLEDBY) && 
            (ce.computed_label_source !== SOURCE.ARIA_LABEL)) {
          this.addLabel(ce, le.computed_label, OpenAjax.a11y.SOURCE.LABEL_REFERENCE);
          le.unused_label = false;          
          le.control_element = ce;
        }  
      }
      else {
        le.unused_label = true;
      }
      
    }
  }
};

/**
 * @method calculateLabelsByEncapsulation
 *
 * @memberOf OpenAjax.a11y.cache.ControlsCache
 *
 * @desc Iterates the list of label elements and calculates the accessible label for
 *       any control elements that are encapsulated by a label element
 */
 
OpenAjax.a11y.cache.ControlsCache.prototype.calculateLabelsByEncapsulation = function () {

  var control_elements = this.control_elements;
  var control_elements_len = control_elements.length;
  
  for (var i = 0; i < control_elements_len; i++) {
 
    var ce = control_elements[i];
 
    switch (ce.control_type) {
  
    case OpenAjax.a11y.CONTROL_TYPE.BUTTON_ELEMENT:
      this.addLabel(ce, this.getElementTextContent(ce, false), OpenAjax.a11y.SOURCE.TEXT_CONTENT);
      break;
  
    default:
    
      // first check if an label exists

      if (ce.computed_label.length === 0 && ce.label_element) {  
        this.addLabel(ce, ce.label_element.computed_label, OpenAjax.a11y.SOURCE.LABEL_ENCAPSULATION);
        ce.label_element.unused_label = false;
        ce.label_element.control_element = ce;
      }
      break;
    } // end switch 
  } // end loop
};

/**
 * @method calculateLabelsByOther
 *
 * @memberOf OpenAjax.a11y.cache.ControlsCache
 *
 * @desc Iterates the list of control elements and calculates the 
 *       accessible label for any control elements that do NOT have 
 *       a computed label, but has a VALUE, ALT or TITLE attribute value 
 */
 
OpenAjax.a11y.cache.ControlsCache.prototype.calculateLabelsByOther = function () {

  var CONTROL_TYPE = OpenAjax.a11y.CONTROL_TYPE;
  
  var control_elements     = this.control_elements;
  var control_elements_len = control_elements.length;
  
  // first check if an label exits
 
  for (var i = 0; i < control_elements_len; i++) {
 
    var ce = control_elements[i];
 
    if (ce.computed_label.length === 0) {
      var de = ce.dom_element;

      switch (ce.control_type) {
  
      case CONTROL_TYPE.BUTTON_INPUT:
        if (ce.value && ce.value.length) {   
          this.addLabel(ce, ce.value, OpenAjax.a11y.SOURCE.VALUE_ATTRIBUTE);        
        }
        else {
          this.addLabel(ce, "", OpenAjax.a11y.SOURCE.NONE);                  
        }
        break;
 
      case CONTROL_TYPE.IMAGE:

        if (de.alt) {
          this.addLabel(ce, de.alt, OpenAjax.a11y.SOURCE.ALT_ATTRIBUTE);        
        }
        else {
          if (de.title && de.title.length) {
            this.addLabel(ce, de.title, OpenAjax.a11y.SOURCE.TITLE_ATTRIBUTE);       
          }
          else {
            this.addLabel(ce, "", OpenAjax.a11y.SOURCE.NONE);      
          }
        }  
        break;

      case CONTROL_TYPE.SUBMIT:

        if (ce.value && ce.value.length) {
          this.addLabel(ce, ce.value, OpenAjax.a11y.SOURCE.VALUE_ATTRIBUTE);        
        }
        else {
          this.addLabel(ce, "SUBMIT", OpenAjax.a11y.SOURCE.BUTTON_TYPE);      
        }  
        break;

      case CONTROL_TYPE.RESET:

        if (ce.value && ce.value.length) {
          this.addLabel(ce, ce.value, OpenAjax.a11y.SOURCE.VALUE_ATTRIBUTE);        
        }
        else {
          this.addLabel(ce, "RESET", OpenAjax.a11y.SOURCE.BUTTON_TYPE);      
        }  
        break;

      default:
    
        if (de.title &&
            de.title.length) {
          // first check if an label exists

          this.addLabel(ce, ce.dom_element.title, OpenAjax.a11y.SOURCE.TITLE_ATTRIBUTE);
        }

        break;
      } // end switch 
    }
  }
};

/**
 * @method calculateControlLabels
 *
 * @memberOf OpenAjax.a11y.cache.ControlsCache
 *
 * @desc Calculates labels for all form controls, based on the order of label 
 *       calculation techniques used by browsers to generate accessible names
 *       for accessibility APIs used by assistive technologies 
 */
 
OpenAjax.a11y.cache.ControlsCache.prototype.calculateControlLabels = function () {

  // These functions are called in the order of overrides
  // Once a control has a label it is ignored by subsequent function calls
  this.calculateLabelsUsingARIA();
  this.calculateLabelsByReference();
  this.calculateLabelsByEncapsulation();
  this.calculateLabelsByOther();
};

/**
 * @method applyAriaOwns
 *
 * @memberOf OpenAjax.a11y.cache.ControlsCache
 *
 * @desc Applies parent/child widget relationships based on the aria-owns property
 *       if aria-owns property is defined for any widgets 
 */
 
OpenAjax.a11y.cache.ControlsCache.prototype.applyAriaOwns = function () {

  var widgets = this.widget_elements;
  var widgets_len = widgets.length;
  
  for (var i = 0; i < widgets_len; i++) {
  
    var widget = widgets[i];
  
    if (widget.has_aria_owns) {
    
//      OpenAjax.a11y.logger.debug("  Owned: " + widget.cache_id);
    
      var ids = widget.getOwnedIds();
      var ids_len = ids.length;
      
      for (var j = 0; j < ids_len; j++) {
         
         var id = ids[j];
        
         var ce = this.getControlElementById(id);
         
         if (ce) {
         
           this.removeFromChildCacheElements(ce);
           widget.addChildControl(ce, true);
           ce.addOwnerControl(widget);
         }
      }    
    }
  
  }

};


/**
 * @method removeFromChildCacheElements
 *
 * @memberOf OpenAjax.a11y.cache.ControlsCache
 *
 * @desc Removes a control from the tree view of form controls and widgets 
 */
 
OpenAjax.a11y.cache.ControlsCache.prototype.removeFromChildCacheElements = function (item) {

  function removeItem(list) {
  
    for (var i = 0; i < list.length; i++ ) {
    
      if (list[i] === item) {
        list = list.splice(i, 1);  
        return true;
      }

      if (list.child_cach_elements && list.child_cach_elements.lengh) {
         if (removeItem(item)) return true;
      }    
    }
    return false;
  }
  
  removeItem(this.child_cache_elements);

};


/* ---------------------------------------------------------------- */
/*                       FormElement                                */ 
/* ---------------------------------------------------------------- */

/**
 * @constructor FormElement
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates a FormElement object used to hold information about form elements
 *
 * @param  {DOMelement}   dom_element   - dom_element object references DOMElement of the form element 
 * @param  {ControlInfo}  control_info  - Information about the parent control cache
 *
 * @property  {DOMElement}  dom_element           - DOMElement associated with the form element
 * @property  {String}      cache_id              - String that uniquely identifies the cache element in the DOMCache
 * @property  {Number}      document_order        - Ordinal position of the form element in the document in relationship to other form elements
 *
 * @property  {Array}       child_cache_elements  - Array of child cache control elements as part of cache control tree 
 * @property  {Number}      control_type          - Constant indicating the type of cache control object  
 *
 * @property  {Boolean}     needs_label    - True if the control needs a label element or aria technique, otherwise false   
 * @property  {Boolean}     has_validity   - True if the control supports validation, otherwise false   
 * @property  {Boolean}     has_pattern    - True if the pattern attribute is defined, otherwise false
 * @property  {Boolean}     is_valid       - True if the pattern is valid, otherwise false
 *
 * @property  {Number}      number_of_controls    - Number of controls in form
 *
 * @property  {String}  action  - The value of the action attribute of the form control
 * @property  {String}  method  - The value of the method attribute of the form control
 * @property  {String}  name_attribute  - The value of the name attribute of the form control
 */

OpenAjax.a11y.cache.FormElement = function (dom_element, control_info) {

  this.dom_element  = dom_element;
  this.child_cache_elements = [];
  this.cache_id     = "";
  this.document_order = 0;

  this.needs_label  = false;
  this.has_validity = false;
  this.has_pattern  = false;
  this.is_valid     = true;

  this.control_type = OpenAjax.a11y.CONTROL_TYPE.FORM;
  this.number_of_controls = 0;
 
  this.action = dom_element.node.action;
  this.method = dom_element.node.method;
  
  this.name_attribute   = dom_element.node.name;
         
};

/**
 * @method addChildControl
 *
 * @memberOf OpenAjax.a11y.cache.FormElement
 * 
 * @desc Adds a cache control element to the tree representation of control elements
 *
 * @param  {WidgetElement | ButtonElement | FieldsetElement | FormElement | InputElement | LabelElement| LegendElement | OptgroupElement | OptionElement | SelectElement | TextareaElement } control_element   - Cache control element object to add 
 */

OpenAjax.a11y.cache.FormElement.prototype.addChildControl = function (child_control) {

  if (child_control) {
   this.child_cache_elements.push(child_control); 
  }  
}; 

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.FormElement
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.FormElement.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.FormElement
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style items
 */

OpenAjax.a11y.cache.FormElement.prototype.getStyle = function () {

  return this.dom_element.getStyle();
  
};


/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.FormElement
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.FormElement.prototype.getAttributes = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  var attributes = this.dom_element.getAttributes();
  
  if (!unsorted) this.dom_element.sortItems(attributes);
  
  return attributes;
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.FormElement
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @return {Array} Returns a array of cache properties
 */

OpenAjax.a11y.cache.FormElement.prototype.getCacheProperties = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  var properties = this.dom_element.getCacheProperties();
  
  return properties;
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.FormElement
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.FormElement.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};


/**
 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.FormElement
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event information
 */

OpenAjax.a11y.cache.FormElement.prototype.getEvents = function (unsorted) {
   
  return this.dom_element.getEvents();
  
};


/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.FormElement
 *
 * @desc Returns a text string representation of the FormElement 
 *
 * @return {String} Returns string represention the FormElement object
 */
 
OpenAjax.a11y.cache.FormElement.prototype.toString = function () {
  var s = "form(";
  
  if (this.number_of_controls === 1) s += "1 control)"; 
  else s += this.number_of_controls + " controls)";
  
  return s;
};

/* ---------------------------------------------------------------- */
/*                       FieldsetElement                            */ 
/* ---------------------------------------------------------------- */

/**
 * @constructor FieldsetElement
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates a FieldsetElement object used to hold information about fieldset elements
 *
 * @param  {DOMelement}   dom_element   - The dom element object representing the fieldset element 
 * @param  {ControlInfo}  control_info  - Information about parent controls
 *
 * @property  {DOMElement}  dom_element     - Reference to the dom element representing the fieldset element
 * @property  {String}      cache_id        - String that uniquely identifies the cache element object in the cache
 * @property  {Number}      document_order  - Ordinal position of the fieldset element in the document in relationship to other fieldset elements
 *
 * @property  {Array}       child_cache_elements  - Array of child cache control elements as part of cache control tree 
 * @property  {Number}      control_type          - Constant indicating the type of cache control object  
 *
 * @property  {Boolean}     needs_label    - True if the control needs a label element or aria technique, otherwise false   
 * @property  {Boolean}     has_validity   - True if the control supports validation, otherwise false   
 * @property  {Boolean}     has_pattern    - True if the pattern attribute is defined, otherwise false
 *
 * @property  {Number}      number_of_controls    - Number of controls in form
 *
 * @property  {FieldsetElement}  fieldset_element  - Reference to any fieldset elements this fieldset is nested in
 * @property  {LegendElement}    legend_element    - Reference to the legend element contained in the fieldset 
 * @property  {Number}           legend_count      - Number of legend elements contained in the fieldset
 */

OpenAjax.a11y.cache.FieldsetElement = function (dom_element, control_info) {

  this.dom_element    = dom_element;
  this.cache_id       = "";
  this.document_order = 0;

  this.needs_label = false;
  this.has_validity = false;
  this.has_pattern = false;

  this.child_cache_elements = [];
  this.control_type = OpenAjax.a11y.CONTROL_TYPE.FIELDSET;
  this.number_of_controls = 0;   
 
  this.fieldset_element = control_info.fieldset_element;
 
  this.legend_element = null;
 
  this.legend_count = 0;
         
};

/**
 * @method addChildControl
 *
 * @memberOf OpenAjax.a11y.cache.FieldsetElement
 * 
 * @desc Adds a cache control element to the tree representation of control elements
 *
 * @param  {WidgetElement | ButtonElement | FieldsetElement | FormElement | InputElement | LabelElement| LegendElement | OptgroupElement | OptionElement | SelectElement | TextareaElement } control_element   - Cache control element object to add 
 */

OpenAjax.a11y.cache.FieldsetElement.prototype.addChildControl = function (child_control) {

  if (child_control) {
    this.child_cache_elements.push(child_control); 
  }  

}; 

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.FieldsetElement
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.FieldsetElement.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.FieldsetElement
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style display objects
 */

OpenAjax.a11y.cache.FieldsetElement.prototype.getStyle = function () {

  return this.dom_element.getStyle();
  
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.FieldsetElement
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of attribute display object
 */

OpenAjax.a11y.cache.FieldsetElement.prototype.getAttributes = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  var attributes = this.dom_element.getAttributes();
  
//  cache_nls.addPropertyIfUndefined(attributes, this, 'tag_name');
  
  if (!unsorted) this.dom_element.sortItems(attributes);
  
  return attributes;
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.FieldsetElement
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of cache property display object
 */

OpenAjax.a11y.cache.FieldsetElement.prototype.getCacheProperties = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;

  var properties = this.dom_element.getCacheProperties(unsorted);

//  cache_nls.addPropertyIfDefined(properties, this, 'tag_name');

  if (!unsorted) this.dom_element.sortItems(properties);

  return properties;
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.FieldsetElement
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.FieldsetElement.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};


/**
 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.FieldsetElement
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event item display objects
 */

OpenAjax.a11y.cache.FieldsetElement.prototype.getEvents = function () {
   
  return this.dom_element.getEvents();
  
};

/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.FieldsetElement
 *
 * @desc Returns a text string representation of the fieldset element 
 *
 * @return {String} Returns string represention the FieldsetElement object
 */
 
OpenAjax.a11y.cache.FieldsetElement.prototype.toString = function () {
 var s = "fieldset(";
   
 if (this.number_of_controls === 1) s += " 1 control)";
 else s += this.number_of_controls + " controls)";
 
 return s;  
     
};

/* ---------------------------------------------------------------- */
/*                       LegendElement                              */ 
/* ---------------------------------------------------------------- */

/**
 * @constructor LegendElement
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates a LegendElement object used to hold information about legend elements
 *
 * @param  {DOMelement}   dom_element   - The dom element object representing the legend element 
 * @param  {ControlInfo}  control_info  - Information about the parent controls
 *
 * @property  {DOMElement}  dom_element     - Reference to the dom element representing the legend element
 * @property  {String}      cache_id        - String that uniquely identifies the cache element object in the cache
 * @property  {Number}      document_order  - Ordinal position of the legend element in the document in relationship to other legend elements
 *
 * @property  {Array}       child_cache_elements  - Array of child cache control elements as part of cache control tree 
 * @property  {Number}      control_type          - Constant indicating the type of cache control object  
 *
 * @property  {Boolean}     needs_label    - True if the control needs a label element or aria technique, otherwise false   
 * @property  {Boolean}     has_validity   - True if the control supports validation, otherwise false   
 * @property  {Boolean}     has_pattern    - True if the pattern attribute is defined, otherwise false
 *
 * @property  {FieldsetElement}  fieldset_element     - Reference to any fieldset elements this legend is nested in
 * @property  {String}           computed_label                - Text content of the legend element 
 * @property  {String}           computed_label_for_comparison - Label for comparison (lowercase, space normalization and trimmed)
 */

OpenAjax.a11y.cache.LegendElement = function (dom_element, control_info) {

  this.dom_element  = dom_element;
  this.cache_id     = "";
  this.document_order = 0;

  this.needs_label = false;
  this.has_validity = false;
  this.has_pattern = false;

  this.child_cache_elements = [];
  this.control_type = OpenAjax.a11y.CONTROL_TYPE.LEGEND;
 
  this.fieldset_element = control_info.fieldset_element;
  
  this.computed_label = "";
  this.computed_label_length = 0;
  this.computed_label_for_comparison = "";

  if (control_info.fieldset_element) {
    control_info.fieldset_element.legend_count++;
  }

};

/**
 * @method addChildControl
 *
 * @memberOf OpenAjax.a11y.cache.LegendElement
 * 
 * @desc Adds a cache control element to the tree representation of control elements
 *
 * @param  {WidgetElement | ButtonElement | FieldsetElement | FormElement | InputElement | LabelElement| LegendElement | OptgroupElement | OptionElement | SelectElement | TextareaElement } control_element   - Cache control element object to add 
 */

OpenAjax.a11y.cache.LegendElement.prototype.addChildControl = function (child_control) {

 if (child_control) {
  this.child_cache_elements.push(child_control); 
 }  

}; 

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.LegendElement
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.LegendElement.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.LegendElement
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style display objects
 */

OpenAjax.a11y.cache.LegendElement.prototype.getStyle = function () {

  return this.dom_element.getStyle();
  
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.LegendElement
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of attribute display object
 */

OpenAjax.a11y.cache.LegendElement.prototype.getAttributes = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  var attributes = this.dom_element.getAttributes();
  
//  cache_nls.addPropertyIfUndefined(attributes, this, 'tag_name');
  
  if (!unsorted) this.dom_element.sortItems(attributes);
  
  return attributes;
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.LegendElement
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of cache property display object
 */

OpenAjax.a11y.cache.LegendElement.prototype.getCacheProperties = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;

  var properties = this.dom_element.getCacheProperties(unsorted);

//  cache_nls.addPropertyIfDefined(properties, this, 'tag_name');

  if (!unsorted) this.dom_element.sortItems(properties);

  return properties;
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.LegendElement
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.LegendElement.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};



/**
 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.LegendElement
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event item display objects
 */

OpenAjax.a11y.cache.LegendElement.prototype.getEvents = function () {
   
  return this.dom_element.getEvents();
  
};
/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.LegendElement
 *
 * @desc Returns a text string representation of the legend element 
 *
 * @return {String} Returns string represention the LegendElement object
 */
 
OpenAjax.a11y.cache.LegendElement.prototype.toString = function () {
 if (this.computed_label.length) 
   return "legend: " + this.computed_label; 
 else
   return "legend: empty"; 
 
};

/* ---------------------------------------------------------------- */
/*                       LabelElement                               */ 
/* ---------------------------------------------------------------- */

/**
 * @constructor LabelElement
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates a LabelElement object used to hold information about label elements
 *
 * @param  {DOMelement}   dom_element   - The dom element object representing the label element 
 * @param  {ControlInfo}  control_info  - Information about the parent controls
 *
 * @property  {DOMElement}  dom_element     - Reference to the dom element representing the label element
 * @property  {String}      cache_id        - String that uniquely identifies the cache element object in the cache
 * @property  {Number}      document_order  - Ordinal position of the label element in the document in relationship to other label elements
 *
 * @property  {Array}       child_cache_elements  - Array of child cache control elements as part of cache control tree 
 * @property  {Number}      control_type          - Constant indicating the type of cache control object  
 *
 * @property  {Boolean}     needs_label    - True if the control needs a label element or aria technique, otherwise false   
 * @property  {Boolean}     has_validity   - True if the control supports validation, otherwise false   
 * @property  {Boolean}     has_pattern    - True if the pattern attribute is defined, otherwise false
 *
 * @property  {String}      computed_label                 - Text content of the label element 
 * @property  {Number}      computed_label_len             - Length of the computed label  
 * @property  {String}      computed_label_for_comparison  - Label for comparison (lowercase, space normalization and trimmed)
 *
 * @property  {Boolean}     unused_label                   - Boolean indicting where the label references a form control 
 * @property  {Object}      control_element    - Reference to the control that the label elements is associated with  
 *
 * @property  {FieldsetElement}  fieldset_element     - Reference to any fieldset elements this label is nested in
 */

OpenAjax.a11y.cache.LabelElement = function (dom_element, control_info) {

 this.dom_element    = dom_element;
 this.cache_id       = "";
 this.document_order = 0;
 
 this.needs_label = false;
 this.has_validity = false;
 this.has_pattern = false;
 
 this.child_cache_elements = [];
 
 this.control_type = OpenAjax.a11y.CONTROL_TYPE.LABEL;

 this.computed_label = "";
 this.computed_label_length = 0;
 this.computed_label_for_comparison = "";

 this.unused_label =  true;
 this.hidden_label = (dom_element.computed_style.is_visible_to_at === OpenAjax.a11y.VISIBILITY.HIDDEN);
 this.control_element =  null;

 this.fieldset_element = control_info.fieldset_element;

 this.for_id = dom_element.node.getAttribute('for');
         
};

/**
 * @method addChildControl
 *
 * @memberOf OpenAjax.a11y.cache.LabelElement
 * 
 * @desc Adds a cache control element to the tree representation of control elements
 *
 * @param  {WidgetElement | ButtonElement | FieldsetElement | FormElement | InputElement | LabelElement| LegendElement | OptgroupElement | OptionElement | SelectElement | TextareaElement } control_element   - Cache control element object to add 
 */

OpenAjax.a11y.cache.LabelElement.prototype.addChildControl = function (child_control) {

 if (child_control) {
  this.child_cache_elements.push(child_control); 
 }  

}; 

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.LabelElement
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.LabelElement.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.LabelElement
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style display objects
 */

OpenAjax.a11y.cache.LabelElement.prototype.getStyle = function () {

  return this.dom_element.getStyle();
  
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.LabelElement
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of attribute display object
 */

OpenAjax.a11y.cache.LabelElement.prototype.getAttributes = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  var attributes = this.dom_element.getAttributes();
  
//  cache_nls.addPropertyIfUndefined(attributes, this, 'for_id');
  
  if (!unsorted) this.dom_element.sortItems(attributes);
  
  return attributes;
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.LabelElement
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of cache property display object
 */

OpenAjax.a11y.cache.LabelElement.prototype.getCacheProperties = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;

  var properties = this.dom_element.getCacheProperties(unsorted);

  cache_nls.addPropertyIfDefined(properties, this, 'computed_label');
  cache_nls.addPropertyIfDefined(properties, this, 'computed_label_for_comparison');

  cache_nls.addPropertyIfDefined(properties, this, 'unused_label');
  cache_nls.addPropertyIfDefined(properties, this, 'hidden_label');

  if (!unsorted) this.dom_element.sortItems(properties);

  return properties;
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.LabelElement
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.LabelElement.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};

/**
 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.LabelElement
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event item display objects
 */

OpenAjax.a11y.cache.LabelElement.prototype.getEvents = function () {
   
  return this.dom_element.getEvents();
  
};
/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.LabelElement
 *
 * @desc Returns a text string representation of the label element 
 *
 * @return {String} Returns string represention the LabelElement object
 */
 
OpenAjax.a11y.cache.LabelElement.prototype.toString = function () {
 if (this.computed_label.length) 
   return "label: " + this.computed_label; 
 else
   return "label: empty"; 
};

/* ---------------------------------------------------------------- */
/*                       InputElement                               */ 
/* ---------------------------------------------------------------- */

/**
 * @constructor InputElement
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates a InputElement object used to hold information about input elements
 *
 * @param  {DOMelement}   dom_element   - The dom element object representing the input element 
 * @param  {ControlInfo}  control_info  - Information about the parent controls
 *
 * @property  {DOMElement}  dom_element     - Reference to the dom element representing the input element
 * @property  {String}      cache_id        - String that uniquely identifies the cache element object in the cache
 * @property  {Number}      document_order  - Ordinal position of the control element in the document in relationship to other control elements
 *
 * @property  {Array}       child_cache_elements  - Array of child cache control elements as part of cache control tree 
 * @property  {String}      type                  - Type of input element  
 * @property  {Number}      control_type          - Constant indicating the type of cache control object  
 * @property  {String}      name_attribute        - Text content of the name attribute  
 *
 * @property  {Boolean}     needs_label    - True if the control needs a label element or aria technique, otherwise false   
 * @property  {Boolean}     has_validity   - True if the control supports validation, otherwise false   
 * @property  {Boolean}     is_valid       - True if the control has a valid value, otherwise false   
 * @property  {Boolean}     has_pattern    - True if the pattern attribute is defined, otherwise false
 *
 * @property  {String}      computed_label                 - Calculated label for the input element 
 * @property  {Number}      computed_label_length          - Length of the label property 
 * @property  {Number}      computed_label_source          - Constant representing how a label was calculated 
 * @property  {String}      computed_label_for_comparison  - Label for comparison (lowercase, space normalization and trimmed)
 *
 * @property  {LabelElement}     label_element    - Reference to any label element that this input is nested in
 * @property  {FieldsetElement}  fieldset_element - Reference to any fieldset elements this input is nested in
 *
 * @property  {String}      readonly   - The value of the readonly attribute 
 * @property  {String}      disabled   - The value of the disabled attribute
 * @property  {String}      value      - The value of the readonly attribute 
 * @property  {String}      checked    - The value of the disabled attribute
 *
 * @property  {Boolean}  is_owned       - True if this control is owned by another widget 
 * @property  {Array}    owner_controls - Array of all the widgets that own this widget (NOTE: More than one owner is an error)    
 */

OpenAjax.a11y.cache.InputElement = function (dom_element, control_info) {

  var node = dom_element.node;

  dom_element.is_interactive = true;

  this.dom_element = dom_element;
  this.cache_id    = "";
  this.document_order = 0;
  
  this.value   = node.value; 
  this.checked = node.checked;

  this.name_attribute = node.getAttribute('name');
  this.required       = node.getAttribute('required');
  this.aria_required  = node.getAttribute('aria-required');
  this.aria_invalid   = node.getAttribute('aria-invalid');

  this.control_type  = OpenAjax.a11y.CONTROL_TYPE.UNKNOWN; 

  this.needs_label  = false;
  this.has_validity = false;
  this.has_pattern  = false;
  this.is_valid = true;  
  
  if ((typeof node.validity === 'object') &&
      (typeof node.validity.valid === 'boolean')) {
    this.is_valid     = node.validity.valid;
  }
  
  var pattern = node.getAttribute('pattern');
  if (pattern && (pattern.length > 0)) this.has_pattern = true;

  var type = node.getAttribute('type'); 
  this.type = type;

  switch (type) {
 
  case 'button':
    this.control_type  = OpenAjax.a11y.CONTROL_TYPE.BUTTON_INPUT; 
    break;

  case 'checkbox':
    this.control_type  = OpenAjax.a11y.CONTROL_TYPE.CHECKBOX; 
    this.needs_label  = true;
    break;

  case 'color':
    this.control_type  = OpenAjax.a11y.CONTROL_TYPE.COLOR; 
    this.needs_label  = true;
    this.has_validity = true;
    this.has_pattern = dom_element.has_pattern;
    break;

  case 'date':
    this.control_type  = OpenAjax.a11y.CONTROL_TYPE.DATE; 
    this.needs_label  = true;
    this.has_validity = true;
    this.has_pattern = dom_element.has_pattern;
    break;

  case 'datetime':
    this.control_type  = OpenAjax.a11y.CONTROL_TYPE.DATETIME; 
    this.needs_label  = true;
    this.has_validity = true;
    this.has_pattern = dom_element.has_pattern;
    break;

  case 'datetime-local':
    this.control_type  = OpenAjax.a11y.CONTROL_TYPE.DATETIME_LOCAL; 
    this.needs_label  = true;
    this.has_validity = true;
    this.has_pattern = dom_element.has_pattern;
    break;

  case 'month':
    this.control_type  = OpenAjax.a11y.CONTROL_TYPE.MONTH; 
    this.needs_label  = true;
    this.has_validity = true;
    this.has_pattern = dom_element.has_pattern;
    break;

  case 'time':
    this.control_type  = OpenAjax.a11y.CONTROL_TYPE.TIME; 
    this.needs_label  = true;
    this.has_validity = true;
    this.has_pattern = dom_element.has_pattern;
    break;

  case 'week':
    this.control_type  = OpenAjax.a11y.CONTROL_TYPE.WEEK; 
    this.needs_label  = true;
    this.has_validity = true;
    this.has_pattern = dom_element.has_pattern;
    break;

  case 'email':
    this.control_type  = OpenAjax.a11y.CONTROL_TYPE.EMAIL; 
    this.needs_label  = true;
    this.has_validity = true;
    this.has_pattern = dom_element.has_pattern;
    break;


  case 'file':
    this.control_type  = OpenAjax.a11y.CONTROL_TYPE.FILE; 
    this.needs_label  = true;
    break;
    
  case 'hidden':
    this.control_type  = OpenAjax.a11y.CONTROL_TYPE.HIDDEN; 
    break;
    
  case 'image':
    this.control_type  = OpenAjax.a11y.CONTROL_TYPE.IMAGE; 
    break;

  case 'number':
    this.control_type  = OpenAjax.a11y.CONTROL_TYPE.NUMBER; 
    this.needs_label  = true;
    this.has_validity = true;
    this.has_pattern = dom_element.has_pattern;
    break;

  case 'password':
    this.control_type  = OpenAjax.a11y.CONTROL_TYPE.PASSWORD; 
    this.needs_label  = true;
    this.has_validity = true;
    this.has_pattern = dom_element.has_pattern;
    break;
    
  case 'radio':
    this.control_type  = OpenAjax.a11y.CONTROL_TYPE.RADIO; 
    this.needs_label  = true;
    break;
    
  case 'range':
    this.control_type  = OpenAjax.a11y.CONTROL_TYPE.RANGE; 
    this.needs_label  = true;
    this.has_validity = true;
    break;
  
  case 'reset':
    this.control_type  = OpenAjax.a11y.CONTROL_TYPE.RESET; 
    break;

  case 'search':
    this.control_type  = OpenAjax.a11y.CONTROL_TYPE.SEARCH; 
    this.needs_label  = true;
    this.has_validity = true;
    this.has_pattern = dom_element.has_pattern;
    break;

  case 'submit':
    this.control_type  = OpenAjax.a11y.CONTROL_TYPE.SUBMIT; 
    break;
    
  case 'tel':
    this.control_type  = OpenAjax.a11y.CONTROL_TYPE.TEL; 
    this.needs_label  = true;
    this.has_validity = true;
    this.has_pattern = dom_element.has_pattern;
    break;

  case 'text':
    this.control_type  = OpenAjax.a11y.CONTROL_TYPE.TEXT; 
    this.needs_label  = true;
    this.has_validity = true;
    this.has_pattern = dom_element.has_pattern;
    break;
    
  case 'url':
    this.control_type  = OpenAjax.a11y.CONTROL_TYPE.RANGE; 
    this.needs_label  = true;
    this.has_validity = true;
    this.has_pattern = dom_element.has_pattern;
    break;
  
  default:
    break; 
  }
 
  this.readonly  = node.readonly;
  this.disabled  = node.disabled;
 
  this.label_element  = control_info.label_element;
  this.fieldset_element = control_info.fieldset_element;

  this.is_owned = false;
  this.owner_controls = [];
  
};

/**
 * @method addOwnerControl
 *
 * @memberOf OpenAjax.a11y.cache.WidgetElement
 * 
 * @desc Adds a ARIA owner control element reference
 *
 * @param  {WidegtElement} owner_control   - Cache control element object to add 
 */

OpenAjax.a11y.cache.InputElement.prototype.addOwnerControl = function (owner_control) {

  if (owner_control) {
   this.is_owned = true;
   this.owner_controls.push(owner_control);
  }  
}; 

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.InputElement
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.InputElement.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.InputElement
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style display objects
 */

OpenAjax.a11y.cache.InputElement.prototype.getStyle = function () {

  return this.dom_element.getStyle();
  
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.InputElement
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of attribute display object
 */

OpenAjax.a11y.cache.InputElement.prototype.getAttributes = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  var attributes = this.dom_element.getAttributes();
  
  if (!unsorted) this.dom_element.sortItems(attributes);
  
  return attributes;
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.InputElement
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of cache property display object
 */

OpenAjax.a11y.cache.InputElement.prototype.getCacheProperties = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;

  var properties = this.dom_element.getCacheProperties(unsorted);

  cache_nls.addPropertyIfDefined(properties, this, 'label');
  cache_nls.addPropertyIfDefined(properties, this, 'computed_label_source');
  cache_nls.addPropertyIfDefined(properties, this, 'label_for_comparison');
  cache_nls.addPropertyIfDefined(properties, this, 'is_widget');
  
  cache_nls.addPropertyIfDefined(properties, this, 'needs_label');
  cache_nls.addPropertyIfDefined(properties, this, 'has_validity');
  cache_nls.addPropertyIfDefined(properties, this, 'has_pattern');
  cache_nls.addPropertyIfDefined(properties, this, 'is_valid');

  if (!unsorted) this.dom_element.sortItems(properties);

  return properties;
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.InputElement
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.InputElement.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};


/**
 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.InputElement
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event item display objects
 */

OpenAjax.a11y.cache.InputElement.prototype.getEvents = function () {
   
  return this.dom_element.getEvents();
  
};

/**
 * @method getLabelNLS
 *
 * @memberOf OpenAjax.a11y.cache.InputElement
 *
 * @desc Returns an object with an NLS localized string and style properties
 *       If label is empty a missing label message will the returned 
 *
 * @return {String | Object} Returns a String if the label has content, 
 *                            but if label is empty it returns an object 
 *                            with a 'label and 'style' property
 */

OpenAjax.a11y.cache.InputElement.prototype.getLabelNLS = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  var label_style = {};
  
  if (this.computed_label_length) {
    return this.computed_label;
  }
  else {
    return cache_nls.getNLSMissingLabelMessage();
  }
  
};

/**
 * @method getLabelSourceNLS
 *
 * @memberOf OpenAjax.a11y.cache.InputElement
 *
 * @desc Returns an object with an NLS localized information on the source of the label
 *
 * @return {String | Object} Returns a String if the label has content, 
 *                            but if label is empty it returns an object 
 *                            with a 'label and 'style' property
 */

OpenAjax.a11y.cache.InputElement.prototype.getLabelSourceNLS = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  return cache_nls.getValueNLS('computed_label_source', this.computed_label_source);
  
};



/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.InputElement
 *
 * @desc Returns a text string representation of the input element 
 *
 * @return {String} Returns string represention the InputElement object
 */
 
OpenAjax.a11y.cache.InputElement.prototype.toString = function () {
  
  return "input[" + this.type + "]";
  
};

/* ---------------------------------------------------------------- */
/*                       ButtonElement                               */ 
/* ---------------------------------------------------------------- */

/**
 * @constructor ButtonElement
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates a ButtonElement object used to hold information about button elements
 *
 * @param  {DOMelement}   dom_element   - The dom element object representing the button element 
 * @param  {ControlInfo}  control_info  - Information about the parent controls
 *
 * @property  {DOMElement}  dom_element  - Reference to the dom element representing the button element
 * @property  {String}      cache_id     - String that uniquely identifies the cache element object in the cache
 *
 * @property  {String}      name_attribute  - Value of the name attribute
 *
 * @property  {Array}       child_cache_elements  - Array of child cache control elements as part of cache control tree 
 * @property  {Number}      control_type          - Constant indicating the type of cache control object  
 *
 * @property  {Boolean}     needs_label    - True if the control needs a label element or aria technique, otherwise false   
 * @property  {Boolean}     has_validity   - True if the control supports validation, otherwise false   
 * @property  {Boolean}     is_valid       - True if the control has a valid value, otherwise false   
 * @property  {Boolean}     has_pattern    - True if the pattern attribute is defined, otherwise false
 *
 * @property  {FieldsetElement}  fieldset_element - Reference to any fieldset elements this button element is nested in
 *
 * @property  {String}     computed_label                  - Calculated label for the button element 
 * @property  {Number}     computed_label_length           - Length of the label property 
 * @property  {String}     computed_ label_for_comparison  - Label for comparison (lowercase, space normalization and trimmed)
 *
 * @property  {String}     readonly              - The value of the readonly attribute 
 * @property  {String}     disabled              - The value of the disabled attribute
 *
 * @property  {Boolean}  is_owned       - True if this control is owned by another widget 
 * @property  {Array}    owner_controls - Array of all the widgets that own this widget (NOTE: More than one owner is an error)    
 */

OpenAjax.a11y.cache.ButtonElement = function (dom_element, control_info) {

  dom_element.is_interactive = true;

  this.dom_element = dom_element;
  this.cache_id    = "";
  
  this.child_cache_elements = [];
 
  var node = dom_element.node;
 
  this.control_type   = OpenAjax.a11y.CONTROL_TYPE.BUTTON_ELEMENT; 
 
  this.name_attribute = node.getAttribute('name');
  
  this.readonly  = node.readonly;
  this.disabled  = node.disabled;
 
  this.fieldset_element = control_info.fieldset_element;

  this.is_owned = false;
  this.owner_controls = [];

  this.needs_label  = false;
  this.has_validity = false;
  this.has_pattern  = false;
  this.is_valid     = true;

};

/**
 * @method addOwnerControl
 *
 * @memberOf OpenAjax.a11y.cache.WidgetElement
 * 
 * @desc Adds a ARIA owner control element reference
 *
 * @param  {WidegtElement} owner_control   - Cache control element object to add 
 */

OpenAjax.a11y.cache.ButtonElement.prototype.addOwnerControl = function (owner_control) {

  if (owner_control) {
   this.is_owned = true;
   this.owner_controls.push(owner_control);
  }  
}; 


/**
 * @method addChildControl
 *
 * @memberOf OpenAjax.a11y.cache.ButtonElement
 * 
 * @desc Adds a cache control element to the tree representation of control elements
 *
 * @param  {WidgetElement | ButtonElement | FieldsetElement | FormElement | InputElement | LabelElement| LegendElement | OptgroupElement | OptionElement | SelectElement | TextareaElement } control_element   - Cache control element object to add 
 */

OpenAjax.a11y.cache.ButtonElement.prototype.addChildControl = function (child_control) {
  if (child_control) {
    this.child_cache_elements.push(child_control); 
  }  
}; 

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.ButtonElement
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.ButtonElement.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.ButtonElement
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style display objects
 */

OpenAjax.a11y.cache.ButtonElement.prototype.getStyle = function () {

  return this.dom_element.getStyle();
  
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.ButtonElement
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of attribute display object
 */

OpenAjax.a11y.cache.ButtonElement.prototype.getAttributes = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  var attributes = this.dom_element.getAttributes();
  
//  cache_nls.addPropertyIfUnefined(attributes, this, 'name');
  
  if (!unsorted) this.dom_element.sortItems(attributes);
  
  return attributes;
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.ButtonElement
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of cache property display object
 */

OpenAjax.a11y.cache.ButtonElement.prototype.getCacheProperties = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;

  var properties = this.dom_element.getCacheProperties(unsorted);

  cache_nls.addPropertyIfDefined(properties, this, 'computed_label');
  cache_nls.addPropertyIfDefined(properties, this, 'computed_label_source');
  cache_nls.addPropertyIfDefined(properties, this, 'computed_label_for_comparison');
  
  cache_nls.addPropertyIfDefined(properties, this, 'needs_label');

  if (!unsorted) this.dom_element.sortItems(properties);

  return properties;
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.ButtonElement
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.ButtonElement.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};


/**
 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.ButtonElement
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event item display objects
 */

OpenAjax.a11y.cache.ButtonElement.prototype.getEvents = function () {
   
  return this.dom_element.getEvents();
  
};

/**
 * @method getLabelNLS
 *
 * @memberOf OpenAjax.a11y.cache.ButtonElement
 *
 * @desc Returns an object with an NLS localized string and style properties
 *       If label is empty a missing label message will the returned 
 *
 * @return {String | Object} Returns a String if the label has content, 
 *                            but if label is empty it returns an object 
 *                            with a 'label and 'style' property
 */

OpenAjax.a11y.cache.ButtonElement.prototype.getLabelNLS = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  var label_style = {};
  
  if (this.computed_label_length) {
    return this.computed_label;
  }
  else {
    return cache_nls.getNLSMissingLabelMessage();
  }
  
};

/**
 * @method getLabelSourceNLS
 *
 * @memberOf OpenAjax.a11y.cache.ButtonElement
 *
 * @desc Returns an object with an NLS localized information on the source of the label
 *
 * @return {String | Object} Returns a String if the label has content, 
 *                            but if label is empty it returns an object 
 *                            with a 'label and 'style' property
 */

OpenAjax.a11y.cache.ButtonElement.prototype.getLabelSourceNLS = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  var label_style = {};
  
  return cache_nls.getValueNLS('computed_label_source', this.computed_label_source);
  
};

/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.ButtonElement
 *
 * @desc Returns a text string representation of the button element 
 *
 * @return {String} Returns string represention the ButtonElement object
 */
 
OpenAjax.a11y.cache.ButtonElement.prototype.toString = function () {
 return "button"; 
};

/* ---------------------------------------------------------------- */
/*                    TextareaElement                               */ 
/* ---------------------------------------------------------------- */

/**
 * @constructor TextareaElement
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates a TextareaElement object used to hold information about textarea elements
 *
 * @param  {DOMelement}   dom_element   - The dom element object representing the textarea element 
 * @param  {ControlInfo}  control_info  - Information about the parent controls
 *
 * @property  {DOMElement}  dom_element     - Reference to the dom element representing the textarea element
 * @property  {String}      cache_id        - String that uniquely identifies the cache element object in the cache
 * @property  {Number}      document_order  - Ordinal position of the control element in the document in relationship to other control elements
 *
 * @property  {String}      name_attribute  - Value of the name attribute
 * @property  {String}      type            - Type of form control
 *
 * @property  {Array}       child_cache_elements  - Array of child cache control elements as part of cache control tree 
 * @property  {Number}      control_type          - Constant indicating the type of cache control object  
 *
 * @property  {Boolean}     needs_label    - True if the control needs a label element or aria technique, otherwise false   
 * @property  {Boolean}     has_validity   - True if the control supports validation, otherwise false   
 * @property  {Boolean}     is_valid       - True if the control has a valid value, otherwise false   
 * @property  {Boolean}     has_pattern    - True if the pattern attribute is defined, otherwise false
 *
 * @property  {String}      computed_label                 - Calculated label for the textarea element 
 * @property  {Number}      computed_label_length          - Length of the label property 
 * @property  {String}      computed_label_for_comparison  - Label for comparison (lowercase, space normalization and trimmed)
 *
 * @property  {LabelElement}     label_element    - Reference to any label element that this input is nested in
 * @property  {FieldsetElement}  fieldset_element - Reference to any fieldset elements this input is nested in
 *
 * @property  {String}      rows       - The value of the rows attribute 
 * @property  {String}      cols       - The value of the cols attribute
 *
 * @property  {String}      readonly   - The value of the readonly attribute 
 * @property  {String}      disabled   - The value of the disabled attribute
 *
 * @property  {Boolean}  is_owned       - True if this control is owned by another widget 
 * @property  {Array}    owner_controls - Array of all the widgets that own this widget (NOTE: More than one owner is an error)    
 */

OpenAjax.a11y.cache.TextareaElement = function (dom_element, control_info) {

  var node = dom_element.node;

  dom_element.is_interactive = true;

  this.dom_element    = dom_element;
  this.cache_id       = "";
  this.document_order = 0;
  this.type = "textarea";  
  
  this.needs_label  = true;
  this.has_validity = true;
  this.has_pattern  = false;
  this.is_valid     = true;
  
  if ((typeof node.validity === 'object') &&
      (typeof node.validity.valid === 'boolean')) {
    this.is_valid     = node.validity.valid;
  }
  
  this.control_type = OpenAjax.a11y.CONTROL_TYPE.TEXTAREA;
 
  this.label_element  = control_info.label_element;
  this.fieldset_element = control_info.fieldset_element;
  
  this.name_attribute  = node.getAttribute('name');
  
  this.rows = node.getAttribute('rows'); 
  this.cols = node.getAttribute('cols'); 
 
  this.readonly  = node.readonly;
  this.disabled  = node.disabled;

  this.is_owned = false;
  this.owner_controls = [];
  
};

/**
 * @method addOwnerControl
 *
 * @memberOf OpenAjax.a11y.cache.WidgetElement
 * 
 * @desc Adds a ARIA owner control element reference
 *
 * @param  {WidegtElement} owner_control   - Cache control element object to add 
 */

OpenAjax.a11y.cache.TextareaElement.prototype.addOwnerControl = function (owner_control) {

  if (owner_control) {
   this.is_owned = true;
   this.owner_controls.push(owner_control);
  }  
}; 

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.TextareaElement
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.TextareaElement.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.TextareaElement
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style display objects
 */

OpenAjax.a11y.cache.TextareaElement.prototype.getStyle = function () {

  return this.dom_element.getStyle();
  
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.TextareaElement
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of attribute display object
 */

OpenAjax.a11y.cache.TextareaElement.prototype.getAttributes = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  var attributes = this.dom_element.getAttributes();
  
  cache_nls.addPropertyIfUndefined(attributes, this, 'rows');
  cache_nls.addPropertyIfUndefined(attributes, this, 'cols');
  
  if (!unsorted) this.dom_element.sortItems(attributes);
  
  return attributes;
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.TextareaElement
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of cache property display object
 */

OpenAjax.a11y.cache.TextareaElement.prototype.getCacheProperties = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;

  var properties = this.dom_element.getCacheProperties(unsorted);

  cache_nls.addPropertyIfDefined(properties, this, 'label');
  cache_nls.addPropertyIfDefined(properties, this, 'computed_label_source');
  cache_nls.addPropertyIfDefined(properties, this, 'label_for_comparison');

  cache_nls.addPropertyIfDefined(properties, this, 'needs_label');

  if (!unsorted) this.dom_element.sortItems(properties);

  return properties;
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.TextareaElement
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.TextareaElement.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};

/**
 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.TextareaElement
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event item display objects
 */

OpenAjax.a11y.cache.TextareaElement.prototype.getEvents = function () {
   
  return this.dom_element.getEvents();
  
};

/**
 * @method getLabelNLS
 *
 * @memberOf OpenAjax.a11y.cache.TextareaElement
 *
 * @desc Returns an object with an NLS localized string and style properties
 *       If label is empty a missing label message will the returned 
 *
 * @return {String | Object} Returns a String if the label has content, 
 *                            but if label is empty it returns an object 
 *                            with a 'label and 'style' property
 */

OpenAjax.a11y.cache.TextareaElement.prototype.getLabelNLS = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  if (this.computed_label_length) {
    return this.computed_label;
  }
  else {
    return cache_nls.getNLSMissingLabelMessage();
  }
  
};


/**
 * @method getLabelSourceNLS
 *
 * @memberOf OpenAjax.a11y.cache.TextareaElement
 *
 * @desc Returns an object with an NLS localized information on the source of the label
 *
 * @return {String | Object} Returns a String if the label has content, 
 *                            but if label is empty it returns an object 
 *                            with a 'label and 'style' property
 */

OpenAjax.a11y.cache.TextareaElement.prototype.getLabelSourceNLS = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;
  return cache_nls.getValueNLS('computed_label_source', this.computed_label_source);
  
};

/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.TextareaElement
 *
 * @desc Returns a text string representation of the textarea element 
 *
 * @return {String} Returns string represention the Element object
 */
 
OpenAjax.a11y.cache.TextareaElement.prototype.toString = function () {
  return "Textarea (" + this.rows + "x" + this.cols + ")"; 
};

/* ---------------------------------------------------------------- */
/*                    ProgressElement                               */ 
/* ---------------------------------------------------------------- */

/**
 * @constructor ProgressElement
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates a ProgressElement object used to hold information about Progress elements
 *
 * @param  {DOMelement}   dom_element   - The dom element object representing the Progress element 
 * @param  {ControlInfo}  control_info  - Information about the parent controls
 *
 * @property  {DOMElement}  dom_element     - Reference to the dom element representing the Progress element
 * @property  {String}      cache_id        - String that uniquely identifies the cache element object in the cache
 * @property  {Number}      document_order  - Ordinal position of the control element in the document in relationship to other control elements
 *
 * @property  {String}      name_attribute  - Value of the name attribute
 * @property  {String}      type            - Type of form control
 *
 * @property  {Array}       child_cache_elements  - Array of child cache control elements as part of cache control tree 
 * @property  {Number}      control_type          - Constant indicating the type of cache control object  
 *
 * @property  {Boolean}     needs_label    - True if the control needs a label element or aria technique, otherwise false   
 * @property  {Boolean}     has_validity   - True if the control supports validation, otherwise false   
 * @property  {Boolean}     is_valid       - True if the control has a valid value, otherwise false   
 * @property  {Boolean}     has_pattern    - True if the pattern attribute is defined, otherwise false
 *
 * @property  {String}      computed_label                 - Calculated label for the Progress element 
 * @property  {Number}      computed_label_length          - Length of the label property 
 * @property  {String}      computed_label_for_comparison  - Label for comparison (lowercase, space normalization and trimmed)
 *
 * @property  {LabelElement}     label_element    - Reference to any label element that this input is nested in
 * @property  {FieldsetElement}  fieldset_element - Reference to any fieldset elements this input is nested in
 *
 * @property  {String}  max   - The value of the max attribute 
 * @property  {String}  value - The value of the value attribute
 *
 * @property  {Boolean}  is_owned       - True if this control is owned by another widget 
 * @property  {Array}    owner_controls - Array of all the widgets that own this widget (NOTE: More than one owner is an error)    
 */

OpenAjax.a11y.cache.ProgressElement = function (dom_element, control_info) {

  var node = dom_element.node;

  dom_element.is_interactive = true;

  this.dom_element    = dom_element;
  this.cache_id       = "";
  this.document_order = 0;
  this.type = "Progress";  
  
  this.needs_label  = true;
  this.has_validity = false;
  this.has_pattern  = false;
  this.is_valid     = true;

  this.control_type = OpenAjax.a11y.CONTROL_TYPE.Progress;
 
  this.label_element  = control_info.label_element;
  this.fieldset_element = control_info.fieldset_element;
  
  this.name_attribute  = node.getAttribute('name');
  
  this.value   = node.getAttribute('value'); 
  this.max     = node.getAttribute('max'); 
  this.percent = 0; 
    
  var d = parseInt(this.max, 10);
  var v = parseInt(this.value, 10);
  
  if ((typeof d === 'number') &&
      (typeof v === 'number') &&
      (d > 0)) this.percent = (100.0 * v) / d;
 
  this.is_owned = false;
  this.owner_controls = [];
  
};

/**
 * @method addOwnerControl
 *
 * @memberOf OpenAjax.a11y.cache.WidgetElement
 * 
 * @desc Adds a ARIA owner control element reference
 *
 * @param  {WidegtElement} owner_control   - Cache control element object to add 
 */

OpenAjax.a11y.cache.ProgressElement.prototype.addOwnerControl = function (owner_control) {

  if (owner_control) {
   this.is_owned = true;
   this.owner_controls.push(owner_control);
  }  
}; 

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.ProgressElement
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.ProgressElement.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.ProgressElement
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style display objects
 */

OpenAjax.a11y.cache.ProgressElement.prototype.getStyle = function () {

  return this.dom_element.getStyle();
  
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.ProgressElement
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of attribute display object
 */

OpenAjax.a11y.cache.ProgressElement.prototype.getAttributes = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  var attributes = this.dom_element.getAttributes();
  
  cache_nls.addPropertyIfUndefined(attributes, this, 'value');
  cache_nls.addPropertyIfUndefined(attributes, this, 'max');
  
  if (!unsorted) this.dom_element.sortItems(attributes);
  
  return attributes;
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.ProgressElement
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of cache property display object
 */

OpenAjax.a11y.cache.ProgressElement.prototype.getCacheProperties = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;

  var properties = this.dom_element.getCacheProperties(unsorted);

  cache_nls.addPropertyIfDefined(properties, this, 'label');
  cache_nls.addPropertyIfDefined(properties, this, 'computed_label_source');
  cache_nls.addPropertyIfDefined(properties, this, 'label_for_comparison');

  cache_nls.addPropertyIfDefined(properties, this, 'needs_label');
  cache_nls.addPropertyIfDefined(properties, this, 'percent');

  if (!unsorted) this.dom_element.sortItems(properties);

  return properties;
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.ProgressElement
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.ProgressElement.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};

/**
 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.ProgressElement
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event item display objects
 */

OpenAjax.a11y.cache.ProgressElement.prototype.getEvents = function () {
   
  return this.dom_element.getEvents();
  
};

/**
 * @method getLabelNLS
 *
 * @memberOf OpenAjax.a11y.cache.ProgressElement
 *
 * @desc Returns an object with an NLS localized string and style properties
 *       If label is empty a missing label message will the returned 
 *
 * @return {String | Object} Returns a String if the label has content, 
 *                            but if label is empty it returns an object 
 *                            with a 'label and 'style' property
 */

OpenAjax.a11y.cache.ProgressElement.prototype.getLabelNLS = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  if (this.computed_label_length) {
    return this.computed_label;
  }
  else {
    return cache_nls.getNLSMissingLabelMessage();
  }
  
};


/**
 * @method getLabelSourceNLS
 *
 * @memberOf OpenAjax.a11y.cache.ProgressElement
 *
 * @desc Returns an object with an NLS localized information on the source of the label
 *
 * @return {String | Object} Returns a String if the label has content, 
 *                            but if label is empty it returns an object 
 *                            with a 'label and 'style' property
 */

OpenAjax.a11y.cache.ProgressElement.prototype.getLabelSourceNLS = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  return cache_nls.getValueNLS('computed_label_source', this.computed_label_source);
  
};

/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.ProgressElement
 *
 * @desc Returns a text string representation of the Progress element 
 *
 * @return {String} Returns string represention the Element object
 */
 
OpenAjax.a11y.cache.ProgressElement.prototype.toString = function () {
 return "Progress(" + this.percent + "%)"; 
};

/* ---------------------------------------------------------------- */
/*                    OutputElement                               */ 
/* ---------------------------------------------------------------- */

/**
 * @constructor OutputElement
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates a OutputElement object used to hold information about Progress elements
 *
 * @param  {DOMelement}   dom_element   - The dom element object representing the Progress element 
 * @param  {ControlInfo}  control_info  - Information about the parent controls
 *
 * @property  {DOMElement}  dom_element     - Reference to the dom element representing the Progress element
 * @property  {String}      cache_id        - String that uniquely identifies the cache element object in the cache
 * @property  {Number}      document_order  - Ordinal position of the control element in the document in relationship to other control elements
 *
 * @property  {String}      name_attribute  - Value of the name attribute
 * @property  {String}      type            - Type of form control
 *
 * @property  {Array}       child_cache_elements  - Array of child cache control elements as part of cache control tree 
 * @property  {Number}      control_type          - Constant indicating the type of cache control object  
 *
 * @property  {Boolean}     needs_label    - True if the control needs a label element or aria technique, otherwise false   
 * @property  {Boolean}     has_validity   - True if the control supports validation, otherwise false   
 * @property  {Boolean}     is_valid       - True if the control has a valid value, otherwise false   
 * @property  {Boolean}     has_pattern    - True if the pattern attribute is defined, otherwise false
 *
 * @property  {String}      computed_label                 - Calculated label for the Progress element 
 * @property  {Number}      computed_label_length          - Length of the label property 
 * @property  {String}      computed_label_for_comparison  - Label for comparison (lowercase, space normalization and trimmed)
 *
 * @property  {LabelElement}     label_element    - Reference to any label element that this input is nested in
 * @property  {FieldsetElement}  fieldset_element - Reference to any fieldset elements this input is nested in
 *
 * @property  {String} value - The value of the value attribute 
 *
 * @property  {Boolean}  is_owned       - True if this control is owned by another widget 
 * @property  {Array}    owner_controls - Array of all the widgets that own this widget (NOTE: More than one owner is an error)    
 */

OpenAjax.a11y.cache.OutputElement = function (dom_element, control_info) {

  var node = dom_element.node;

  dom_element.is_interactive = true;

  this.dom_element    = dom_element;
  this.cache_id       = "";
  this.document_order = 0;
  this.type = "Progress";  
  
  this.needs_label  = true;
  this.has_validity = false;
  this.has_pattern  = false;
  this.is_valid     = true;

  this.control_type = OpenAjax.a11y.CONTROL_TYPE.Output;
 
  this.label_element  = control_info.label_element;
  this.fieldset_element = control_info.fieldset_element;
  
  this.value = node.getAttribute('value'); 

  this.is_owned = false;
  this.owner_controls = [];
  
};

/**
 * @method addOwnerControl
 *
 * @memberOf OpenAjax.a11y.cache.WidgetElement
 * 
 * @desc Adds a ARIA owner control element reference
 *
 * @param  {WidegtElement} owner_control   - Cache control element object to add 
 */

OpenAjax.a11y.cache.OutputElement.prototype.addOwnerControl = function (owner_control) {

  if (owner_control) {
   this.is_owned = true;
   this.owner_controls.push(owner_control);
  }  
}; 

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.OutputElement
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.OutputElement.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.OutputElement
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style display objects
 */

OpenAjax.a11y.cache.OutputElement.prototype.getStyle = function () {

  return this.dom_element.getStyle();
  
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.OutputElement
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of attribute display object
 */

OpenAjax.a11y.cache.OutputElement.prototype.getAttributes = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  var attributes = this.dom_element.getAttributes();
  
  cache_nls.addPropertyIfUndefined(attributes, this, 'value');
  
  if (!unsorted) this.dom_element.sortItems(attributes);
  
  return attributes;
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.OutputElement
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of cache property display object
 */

OpenAjax.a11y.cache.OutputElement.prototype.getCacheProperties = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;

  var properties = this.dom_element.getCacheProperties(unsorted);

  cache_nls.addPropertyIfDefined(properties, this, 'label');
  cache_nls.addPropertyIfDefined(properties, this, 'computed_label_source');
  cache_nls.addPropertyIfDefined(properties, this, 'label_for_comparison');

  cache_nls.addPropertyIfDefined(properties, this, 'needs_label');

  if (!unsorted) this.dom_element.sortItems(properties);

  return properties;
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.OutputElement
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.OutputElement.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};

/**
 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.OutputElement
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event item display objects
 */

OpenAjax.a11y.cache.OutputElement.prototype.getEvents = function () {
   
  return this.dom_element.getEvents();
  
};

/**
 * @method getLabelNLS
 *
 * @memberOf OpenAjax.a11y.cache.OutputElement
 *
 * @desc Returns an object with an NLS localized string and style properties
 *       If label is empty a missing label message will the returned 
 *
 * @return {String | Object} Returns a String if the label has content, 
 *                            but if label is empty it returns an object 
 *                            with a 'label and 'style' property
 */

OpenAjax.a11y.cache.OutputElement.prototype.getLabelNLS = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  if (this.computed_label_length) {
    return this.computed_label;
  }
  else {
    return cache_nls.getNLSMissingLabelMessage();
  }
  
};


/**
 * @method getLabelSourceNLS
 *
 * @memberOf OpenAjax.a11y.cache.OutputElement
 *
 * @desc Returns an object with an NLS localized information on the source of the label
 *
 * @return {String | Object} Returns a String if the label has content, 
 *                            but if label is empty it returns an object 
 *                            with a 'label and 'style' property
 */

OpenAjax.a11y.cache.OutputElement.prototype.getLabelSourceNLS = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  return cache_nls.getValueNLS('computed_label_source', this.computed_label_source);
  
};

/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.OutputElement
 *
 * @desc Returns a text string representation of the Progress element 
 *
 * @return {String} Returns string represention the Element object
 */
 
OpenAjax.a11y.cache.OutputElement.prototype.toString = function () {
 return "Output(" + this.value + ")"; 
};


/* ---------------------------------------------------------------- */
/*                    MeterElement                               */ 
/* ---------------------------------------------------------------- */

/**
 * @constructor MeterElement
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates a MeterElement object used to hold information about Progress elements
 *
 * @param  {DOMelement}   dom_element   - The dom element object representing the Progress element 
 * @param  {ControlInfo}  control_info  - Information about the parent controls
 *
 * @property  {DOMElement}  dom_element     - Reference to the dom element representing the Progress element
 * @property  {String}      cache_id        - String that uniquely identifies the cache element object in the cache
 * @property  {Number}      document_order  - Ordinal position of the control element in the document in relationship to other control elements
 *
 * @property  {String}      name_attribute  - Value of the name attribute
 * @property  {String}      type            - Type of form control
 *
 * @property  {Array}       child_cache_elements  - Array of child cache control elements as part of cache control tree 
 * @property  {Number}      control_type          - Constant indicating the type of cache control object  
 *
 * @property  {Boolean}     needs_label    - True if the control needs a label element or aria technique, otherwise false   
 * @property  {Boolean}     has_validity   - True if the control supports validation, otherwise false   
 * @property  {Boolean}     is_valid       - True if the control has a valid value, otherwise false   
 * @property  {Boolean}     has_pattern    - True if the pattern attribute is defined, otherwise false
 *
 * @property  {String}      computed_label                 - Calculated label for the Progress element 
 * @property  {Number}      computed_label_length          - Length of the label property 
 * @property  {String}      computed_label_for_comparison  - Label for comparison (lowercase, space normalization and trimmed)
 *
 * @property  {LabelElement}     label_element    - Reference to any label element that this input is nested in
 * @property  {FieldsetElement}  fieldset_element - Reference to any fieldset elements this input is nested in
 *
 * @property  {String} max   - The value of the max attribute 
 * @property  {String} max   - The value of the min attribute 
 * @property  {String} value - The value of the value attribute 
 *
 * @property  {Boolean}  is_owned       - True if this control is owned by another widget 
 * @property  {Array}    owner_controls - Array of all the widgets that own this widget (NOTE: More than one owner is an error)    
 */

OpenAjax.a11y.cache.MeterElement = function (dom_element, control_info) {

  var node = dom_element.node;

  dom_element.is_interactive = true;

  this.dom_element    = dom_element;
  this.cache_id       = "";
  this.document_order = 0;
  this.type = "Progress";  
  
  this.needs_label  = true;
  this.has_validity = false;
  this.has_pattern  = false;
  this.is_valid     = true;

  this.control_type = OpenAjax.a11y.CONTROL_TYPE.METER;
 
  this.label_element  = control_info.label_element;
  this.fieldset_element = control_info.fieldset_element;
  
  this.min   = node.getAttribute('min');
  this.max   = node.getAttribute('max'); 
  this.value = node.getAttribute('value'); 

  this.is_owned = false;
  this.owner_controls = [];
  
};

/**
 * @method addOwnerControl
 *
 * @memberOf OpenAjax.a11y.cache.WidgetElement
 * 
 * @desc Adds a ARIA owner control element reference
 *
 * @param  {WidegtElement} owner_control   - Cache control element object to add 
 */

OpenAjax.a11y.cache.MeterElement.prototype.addOwnerControl = function (owner_control) {

  if (owner_control) {
   this.is_owned = true;
   this.owner_controls.push(owner_control);
  }  
}; 

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.MeterElement
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.MeterElement.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.MeterElement
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style display objects
 */

OpenAjax.a11y.cache.MeterElement.prototype.getStyle = function () {

  return this.dom_element.getStyle();
  
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.MeterElement
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of attribute display object
 */

OpenAjax.a11y.cache.MeterElement.prototype.getAttributes = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  var attributes = this.dom_element.getAttributes();
  
  cache_nls.addPropertyIfUndefined(attributes, this, 'value');
  cache_nls.addPropertyIfUndefined(attributes, this, 'max');
  cache_nls.addPropertyIfUndefined(attributes, this, 'min');
  
  if (!unsorted) this.dom_element.sortItems(attributes);
  
  return attributes;
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.MeterElement
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of cache property display object
 */

OpenAjax.a11y.cache.MeterElement.prototype.getCacheProperties = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;

  var properties = this.dom_element.getCacheProperties(unsorted);

  cache_nls.addPropertyIfDefined(properties, this, 'label');
  cache_nls.addPropertyIfDefined(properties, this, 'computed_label_source');
  cache_nls.addPropertyIfDefined(properties, this, 'label_for_comparison');

  cache_nls.addPropertyIfDefined(properties, this, 'needs_label');
  cache_nls.addPropertyIfDefined(properties, this, 'value');

  if (!unsorted) this.dom_element.sortItems(properties);

  return properties;
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.MeterElement
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.MeterElement.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};

/**
 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.MeterElement
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event item display objects
 */

OpenAjax.a11y.cache.MeterElement.prototype.getEvents = function () {
   
  return this.dom_element.getEvents();
  
};

/**
 * @method getLabelNLS
 *
 * @memberOf OpenAjax.a11y.cache.MeterElement
 *
 * @desc Returns an object with an NLS localized string and style properties
 *       If label is empty a missing label message will the returned 
 *
 * @return {String | Object} Returns a String if the label has content, 
 *                            but if label is empty it returns an object 
 *                            with a 'label and 'style' property
 */

OpenAjax.a11y.cache.MeterElement.prototype.getLabelNLS = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  if (this.computed_label_length) {
    return this.computed_label;
  }
  else {
    return cache_nls.getNLSMissingLabelMessage();
  }
  
};

/**
 * @method getLabelSourceNLS
 *
 * @memberOf OpenAjax.a11y.cache.MeterElement
 *
 * @desc Returns an object with an NLS localized information on the source of the label
 *
 * @return {String | Object} Returns a String if the label has content, 
 *                            but if label is empty it returns an object 
 *                            with a 'label and 'style' property
 */

OpenAjax.a11y.cache.MeterElement.prototype.getLabelSourceNLS = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  return cache_nls.getValueNLS('computed_label_source', this.computed_label_source);
  
};

/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.MeterElement
 *
 * @desc Returns a text string representation of the Progress element 
 *
 * @return {String} Returns string represention the Element object
 */
 
OpenAjax.a11y.cache.MeterElement.prototype.toString = function () {
 return "Meter(" + this.value + ")"; 
};

/* ---------------------------------------------------------------- */
/*                      SelectElement                               */ 
/* ---------------------------------------------------------------- */

/**
 * @constructor SelectElement
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates a SelectElement object used to hold information about select elements
 *
 * @param  {DOMelement}   dom_element   - The dom element object representing the select element 
 * @param  {ControlInfo}  control_info  - Information about the parent controls
 *
 * @property  {DOMElement}  dom_element     - Reference to the dom element representing the select element
 * @property  {String}      cache_id        - String that uniquely identifies the cache element object in the cache
 * @property  {Number}      document_order  - Ordinal position of the control element in the document in relationship to other control elements
 *
 * @property  {String}      name_attribute  - Value of the name attribute
 * @property  {String}      type            - String indicating the type of form control
 *
 * @property  {Array}       child_cache_elements  - Array of child cache control elements as part of cache control tree 
 * @property  {Array}       option_elements       - Array of child cache option elements  
 * @property  {Number}      control_type          - Constant indicating the type of cache control object  
 *
 * @property  {Boolean}     needs_label    - True if the control needs a label element or aria technique, otherwise false   
 * @property  {Boolean}     has_validity   - True if the control supports validation, otherwise false   
 * @property  {Boolean}     is_valid       - True if the control has a valid value, otherwise false   
 * @property  {Boolean}     has_pattern    - True if the pattern attribute is defined, otherwise false
 *
 * @property  {LabelElement}     label_element    - Reference to any label element that this input is nested in
 * @property  {FieldsetElement}  fieldset_element - Reference to any fieldset elements this select element is nested in
 *
 * @property  {String}      computed_label                 - Calculated label for the select element 
 * @property  {Number}      computed_label_length          - Length of the label property 
 * @property  {String}      computed_label_for_comparison  - Label for comparison (lowercase, space normalization and trimmed)
 * @property  {String}      size                  - The value of the size attribute 
 * @property  {String}      multiple              - The value of the multiple attribute
 *
 * @property  {Boolean}  is_owned       - True if this control is owned by another widget 
 * @property  {Array}    owner_controls - Array of all the widgets that own this widget (NOTE: More than one owner is an error)    
 */

OpenAjax.a11y.cache.SelectElement = function (dom_element, control_info) {

  dom_element.is_interactive = true;

  this.dom_element    = dom_element;
  
  this.cache_id       = "";
  this.document_order = 0;
  
  this.child_cache_elements = [];
  
  this.option_elements = [];
 
  this.control_type = OpenAjax.a11y.CONTROL_TYPE.SELECT;

  var node = dom_element.node;

  this.name_attribute  = node.getAttribute('name');
  this.type = "select";
  
  this.size   = node.size;
  this.multiple = node.multiple;
 
  this.label_element  = control_info.label_element;
  this.fieldset_element = control_info.fieldset_element;
  
  this.is_owned = false;
  this.owner_controls = [];

  this.needs_label  = true;
  this.has_validity = false;
  this.has_pattern  = false;
  this.is_valid     = true;
  
  if ((typeof node.validity === 'object') &&
      (typeof node.validity.valid === 'boolean')) {
    this.is_valid     = node.validity.valid;
  }
  
};

/**
 * @method addChildControl
 *
 * @memberOf OpenAjax.a11y.cache.SelectElement
 * 
 * @desc Adds a cache control element to the tree representation of control elements
 *
 * @param  {WidgetElement | ButtonElement | FieldsetElement | FormElement | InputElement | LabelElement| LegendElement | OptgroupElement | OptionElement | SelectElement | TextareaElement } control_element   - Cache control element object to add 
 */

OpenAjax.a11y.cache.SelectElement.prototype.addChildControl = function (child_control) {

 if (child_control) {
  this.child_cache_elements.push(child_control); 
 }  

};

/**
 * addOption
 * 
 * @desc add a OptionElement object reference to the tree of   
 *
 * @param  child_control    Object control cache element object  
 *
 * @return  nothing
 */

OpenAjax.a11y.cache.SelectElement.prototype.addOption = function (option_element) {

  if (option_element) {
    this.option_elements.push(option_element); 
    option_element.document_order = this.option_elements.length;
    option_element.cache_id    = this.cache_id + "_" + this.option_elements.length;
  }  

}; 

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.SelectElement
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.SelectElement.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.SelectElement
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style display objects
 */

OpenAjax.a11y.cache.SelectElement.prototype.getStyle = function () {

  return this.dom_element.getStyle();
  
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.SelectElement
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of attribute display object
 */

OpenAjax.a11y.cache.SelectElement.prototype.getAttributes = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  var attributes = this.dom_element.getAttributes();
  
//  cache_nls.addPropertyIfUndefined(attributes, this, 'tag_name');
  
  if (!unsorted) this.dom_element.sortItems(attributes);
  
  return attributes;
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.SelectElement
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of cache property display object
 */

OpenAjax.a11y.cache.SelectElement.prototype.getCacheProperties = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;

  var properties = this.dom_element.getCacheProperties(unsorted);

  cache_nls.addPropertyIfDefined(properties, this, 'label');
  cache_nls.addPropertyIfDefined(properties, this, 'computed_label_source');
  cache_nls.addPropertyIfDefined(properties, this, 'label_for_comparison');
  
  cache_nls.addPropertyIfDefined(properties, this, 'needs_label');

  if (!unsorted) this.dom_element.sortItems(properties);

  return properties;
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.SelectElement
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.SelectElement.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};


/**
 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.SelectElement
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event item display objects
 */

OpenAjax.a11y.cache.SelectElement.prototype.getEvents = function () {
   
  return this.dom_element.getEvents();
  
};

/**
 * @method getLabelNLS
 *
 * @memberOf OpenAjax.a11y.cache.SelectElement
 *
 * @desc Returns an object with an NLS localized string and style properties
 *       If label is empty a missing label message will the returned 
 *
 * @return {String | Object} Returns a String if the label has content, 
 *                            but if label is empty it returns an object 
 *                            with a 'label and 'style' property
 */

OpenAjax.a11y.cache.SelectElement.prototype.getLabelNLS = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  var label_style = {};
  
  if (this.computed_label_length) {
    return this.computed_label;
  }
  else {
    return cache_nls.getNLSMissingLabelMessage();
  }
  
};


/**
 * @method getLabelSourceNLS
 *
 * @memberOf OpenAjax.a11y.cache.SelectElement
 *
 * @desc Returns an object with an NLS localized information on the source of the label
 *
 * @return {String | Object} Returns a String if the label has content, 
 *                            but if label is empty it returns an object 
 *                            with a 'label and 'style' property
 */

OpenAjax.a11y.cache.SelectElement.prototype.getLabelSourceNLS = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  var label_style = {};
  
  return cache_nls.getValueNLS('computed_label_source', this.computed_label_source);
  
};

/**
 * @method addOwnerControl
 *
 * @memberOf OpenAjax.a11y.cache.WidgetElement
 * 
 * @desc Adds a ARIA owner control element reference
 *
 * @param  {WidegtElement} owner_control   - Cache control element object to add 
 */

OpenAjax.a11y.cache.SelectElement.prototype.addOwnerControl = function (owner_control) {

  if (owner_control) {
   this.is_owned = true;
   this.owner_controls.push(owner_control);
  }  
}; 

/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.SelectElement
 *
 * @desc Returns a text string representation of the select element 
 *
 * @return {String} Returns string represention the SelectElement object
 */
 
OpenAjax.a11y.cache.SelectElement.prototype.toString = function () {
  return "select: " + this.option_elements.length + " options"; 
};

/* ---------------------------------------------------------------- */
/*                       OptgroupElement                               */ 
/* ---------------------------------------------------------------- */

/**
 * @constructor OptgroupElement
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates a OptgroupElement object used to hold information about optgroup elements
 *
 * @param  {DOMelement}   dom_element   - The dom element object representing the optgroup element 
 * @param  {ControlInfo}  control_info  - Information about the parent controls
 *
 * @property  {DOMElement}  dom_element  - Reference to the dom element representing the optgroup element
 * @property  {String}      cache_id     - String that uniquely identifies the cache element object in the cache
 *
 * @property  {Array}       child_cache_elements  - Array of child cache control elements as part of cache control tree 
 * @property  {Number}      control_type          - Constant indicating the type of cache control object  
 *
 * @property  {SelectElement}  select_element     - Reference to the select element that this optgroup is nested in
 *
 * @property  {String}      computed_label                 - Calculated label for the select element 
 * @property  {Number}      computed_label_length          - Length of the label property 
 * @property  {String}      computed_label_for_comparison  - Label for comparison (lowercase, space normalization and trimmed)
 */

OpenAjax.a11y.cache.OptgroupElement = function (dom_element, control_info) {

 this.dom_element = dom_element;
 this.cache_id    = "";
 
 this.child_cache_elements = [];
         
 this.control_type = OpenAjax.a11y.CONTROL_TYPE.OPTGROUP;
 
 this.select_element = control_info.select_element;
         
 this.computed_label = dom_element.node.computed_label;
 if (this.computed_label) { 
   this.computed_label_length = this.computed_label.length;
   this.computed_label_for_comparison = this.computed_label.normalizeSpace().toLowerCase();
 }  
 else {
   this.computed_label_length = 0;
   this.computed_label_for_comparison = "";
 }  
 
};

/**
 * @method addChildControl
 *
 * @memberOf OpenAjax.a11y.cache.OptgroupElement
 * 
 * @desc Adds a cache control element to the tree representation of control elements
 *
 * @param  {WidgetElement | ButtonElement | FieldsetElement | FormElement | InputElement | LabelElement| LegendElement | OptgroupElement | OptionElement | SelectElement | TextareaElement } control_element   - Cache control element object to add 
 */

OpenAjax.a11y.cache.OptgroupElement.prototype.addChildControl = function (child_control) {

 if (child_control) {
  this.child_cache_elements.push(child_control); 
 }  

}; 

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.OptgroupElement
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.OptgroupElement.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.OptgroupElement
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style display objects
 */

OpenAjax.a11y.cache.OptgroupElement.prototype.getStyle = function () {

  return this.dom_element.getStyle();
  
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.OptgroupElement
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of attribute display object
 */

OpenAjax.a11y.cache.OptgroupElement.prototype.getAttributes = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  var attributes = this.dom_element.getAttributes();
  
//  cache_nls.addPropertyIfUndefined(attributes, this, 'tag_name');
  
  if (!unsorted) this.dom_element.sortItems(attributes);
  
  return attributes;
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.OptgroupElement
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of cache property display object
 */

OpenAjax.a11y.cache.OptgroupElement.prototype.getCacheProperties = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;

  var properties = this.dom_element.getCacheProperties(unsorted);

  cache_nls.addPropertyIfDefined(properties, this, 'label');
  cache_nls.addPropertyIfDefined(properties, this, 'computed_label_source');
  cache_nls.addPropertyIfDefined(properties, this, 'label_for_comparison');

  if (!unsorted) this.dom_element.sortItems(properties);

  return properties;
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.OptgroupElement
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.OptgroupElement.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};


/**
 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.OptgroupElement
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event item display objects
 */

OpenAjax.a11y.cache.OptgroupElement.prototype.getEvents = function () {
   
  return this.dom_element.getEvents();
  
};
/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.OptgroupElement
 *
 * @desc Returns a text string representation of the optgroup element 
 *
 * @return {String} Returns string represention the OptgroupElement object
 */
 
OpenAjax.a11y.cache.OptgroupElement.prototype.toString = function () {
 return "OPTGROUP with " + this.child_cache_elements.length + " options"; 
};

/* ---------------------------------------------------------------- */
/*                      OptionElement                               */ 
/* ---------------------------------------------------------------- */

/**
 * @constructor OptionElement
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates a OptgroupElement object used to hold information about optgroup elements
 *
 * @param  {DOMelement}   dom_element   - The dom element object representing the optgroup element 
 * @param  {ControlInfo}  control_info  - Information about the parent controls
 *
 * @property  {DOMElement}  dom_element        - Reference to the dom element representing the optgroup element
 * @property  {String}      cache_id     - String that uniquely identifies the cache element object in the cache
 *
 * @property  {Number}      control_type       - Constant indicating the type of cache control object  
 *
 * @property  {SelectElement}  select_element  - Reference to the select element that this optgroup is nested in
 *
 * @property  {String}         value           - Value of the value attribute 
 */

OpenAjax.a11y.cache.OptionElement = function (dom_element, control_info) {

 this.dom_element = dom_element;
 this.cache_id    = "";
 
 this.control_type   = OpenAjax.a11y.CONTROL_TYPE.OPTION;
 
 this.select_element = control_info.select_element;
         
 this.value = dom_element.node.value;
 
};

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.OptionElement
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.OptionElement.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.OptionElement
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style display objects
 */

OpenAjax.a11y.cache.OptionElement.prototype.getStyle = function () {

  return this.dom_element.getStyle();
  
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.OptionElement
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of attribute display object
 */

OpenAjax.a11y.cache.OptionElement.prototype.getAttributes = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  var attributes = this.dom_element.getAttributes();
  
//  cache_nls.addPropertyIfUndefined(attributes, this, 'tag_name');
  
  if (!unsorted) this.dom_element.sortItems(attributes);
  
  return attributes;
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.OptionElement
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of cache property display object
 */

OpenAjax.a11y.cache.OptionElement.prototype.getCacheProperties = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;

  var properties = this.dom_element.getCacheProperties(unsorted);

  cache_nls.addPropertyIfDefined(properties, this, 'label');
  cache_nls.addPropertyIfDefined(properties, this, 'computed_label_source');
  cache_nls.addPropertyIfDefined(properties, this, 'label_for_comparison');

  if (!unsorted) this.dom_element.sortItems(properties);

  return properties;
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.OptionElement
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.OptionElement.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};

/**

 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.OptionElement
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event item display objects
 */

OpenAjax.a11y.cache.OptionElement.prototype.getEvents = function () {
   
  return this.dom_element.getEvents();
  
};

/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.OptionElement
 *
 * @desc Returns a text string representation of the option element 
 *
 * @return {String} Returns string represention the OptionElement object
 */
 
OpenAjax.a11y.cache.OptionElement.prototype.toString = function () {
 return "OPTION with value=" + this.value; 
};

/* ---------------------------------------------------------------- */
/*                       WidgetElement                               */ 
/* ---------------------------------------------------------------- */

/**
 * @constructor WidgetElement
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates a InputElement object used to hold information about input elements
 *
 * @param  {DOMelement}   dom_element   - The dom element object representing the input element 
 * @param  {ControlInfo}  control_info  - Information about the parent controls
 *
 * @property  {DOMElement}  dom_element     - Reference to the dom element representing the input element
 * @property  {String}      cache_id        - String that uniquely identifies the cache element object in the cache
 * @property  {Number}      document_order  - Ordinal position of the control element in the document in relationship to other control elements
 *
 * @property  {Boolean}     has_aria_owns              - if the widget has aria-owns, use this to calculate children 
 * @property  {Array}       child_cache_elements  - Array of child cache control elements as part of cache control tree 
 * @property  {String}      type                  - String indicating the type of input element  
 * @property  {Number}      control_type          - Constant indicating the type of cache control object 
 *
 * @property  {Boolean}     needs_label    - True if the control needs a label element or aria technique, otherwise false   
 * @property  {Boolean}     has_validity   - True if the control supports validation, otherwise false   
 * @property  {Boolean}     is_valid       - True if the control has a valid value, otherwise false   
 * @property  {Boolean}     has_pattern    - True if the pattern attribute is defined, otherwise false
 * 
 * @property  {String}      name_attribute        - Text content of the name attribute  
 *
 * @property  {String}  computed_label                 - Calculated label for the input element 
 * @property  {Number}  computed_label_length          - Length of the label property 
 * @property  {Number}  computed_label_source          - Constant representing how a label was calculated 
 * @property  {String}  computed_label_for_comparison  - Label for comparison (lowercase, space normalization and trimmed)
 *
 * @property  {LabelElement}     label_element    - Reference to any label element that this input is nested in
 * @property  {FieldsetElement}  fieldset_element - Reference to any fieldset elements this input is nested in
 *
 * @property  {String}  readonly  - The value of the readonly attribute 
 * @property  {String}  disabled  - The value of the disabled attribute
 * @property  {String}  value     - The value of the readonly attribute 
 * @property  {String}  checked   - The value of the disabled attribute
 *
 * @property  {Boolean}  is_owned       - True if this widget is owned by another widget 
 * @property  {Array}    owner_controls - Array of all the widgets that own this widget (NOTE: More than one owner is an error)    
 */

OpenAjax.a11y.cache.WidgetElement = function (dom_element, control_info) {

  var node = dom_element.node;
 
  this.dom_element    = dom_element;
  this.has_aria_owns       = dom_element.hasOwns();
  this.cache_id       = "";
  this.document_order = 0;
  this.parent_widget  = control_info.parent_widget;
  
  this.child_cache_elements = [];
  this.type    = node.type; 
  this.value   = node.value; 
  this.checked = node.checked;

  this.name_attribute = node.getAttribute('name');
  this.required       = node.getAttribute('required');
  this.aria_required  = node.getAttribute('aria-required');
  this.aria_invalid   = node.getAttribute('aria-invalid');

  this.control_type   = OpenAjax.a11y.CONTROL_TYPE.WIDGET; 
  
  this.label_element    = control_info.label_element;
  this.fieldset_element = control_info.fieldset_element;

  this.aria_attributes_with_invalid_values  = [];
  this.aria_attributes_missing              = []; 
  
  this.is_owned = false;
  this.owner_controls = [];

  role_info = OpenAjax.a11y.aria.getRoleObject(dom_element.role);
  
  this.needs_label  = false;
  this.has_validity = false;
  this.has_pattern  = false;
  this.is_valid     = true;

  if (role_info && role_info.reqName) this.needs_label  = true;
  
};

/**
 * @method addChildControl
 *
 * @memberOf OpenAjax.a11y.cache.WidgetElement
 * 
 * @desc Adds a cache control element to the tree representation of control elements
 *
 * @param  {WidegtElement | ButtonElement | FieldsetElement | FormElement | InputElement | LabelElement| LegendElement | OptgroupElement | OptionElement | SelectElement | TextareaElement } control_element   - Cache control element object to add 
 * @param  {Boolean} override_owns  - If true, allow child elements to be added if the widget has an owns property
 */

OpenAjax.a11y.cache.WidgetElement.prototype.addChildControl = function (child_control, override_owns) {

  if (this.has_aria_owns && ((typeof override_owns != 'boolean') || ((typeof override_owns === 'boolean') && !override_owns))) return;

  if (child_control) {
   this.child_cache_elements.push(child_control);
  }  
}; 

/**
 * @method addOwnerControl
 *
 * @memberOf OpenAjax.a11y.cache.WidgetElement
 * 
 * @desc Adds a ARIA owner control element reference
 *
 * @param  {WidegtElement} owner_control   - Cache control element object to add 
 */

OpenAjax.a11y.cache.WidgetElement.prototype.addOwnerControl = function (owner_control) {

  if (owner_control) {
   this.is_owned = true;
   this.parent_widget = owner_control;
   this.owner_controls.push(owner_control);
  }  
}; 

/**
 * @method getOwnedIds
 *
 * @memberOf OpenAjax.a11y.cache.WidgetElement
 * 
 * @desc Returns an array of strings representing the ids in the aria-owns property
 *
 * @return {Array} Returns an array of string objects represrenting the ids of the aria-owns property
 */

OpenAjax.a11y.cache.WidgetElement.prototype.getOwnedIds = function () {

  var aria_owns = this.dom_element.aria_owns;
  var return_array = [];

  if (typeof aria_owns === 'string' && (aria_owns.length > 0)) {
  
    if (aria_owns.indexOf(' ') > 0) {
      return_array = aria_owns.split(' ');
    }
    else {
      return_array.push(aria_owns);
    }
    
  }
  
  return return_array;

}; 

/**
 * @method hasChildRole
 *
 * @memberOf OpenAjax.a11y.cache.WidgetElement
 *
 * @desc Tests if a widget has a child ARIA element with a certain role
 *
 * @param {String}  role -  Role to find 
 *
 * @return {Boolean} Returns true if widget has child element with role, otherwise false
 */

OpenAjax.a11y.cache.WidgetElement.prototype.hasChildRole = function (role) {

   function checkCacheChildren(list) {
   
     var flag = false;
   
     for (var i = 0; (i < list.length); i++) {
     
       var item = list[i];
     
       if (item.dom_element.role === role) {
         flag = true;
         break;
       }
       else {
         if (item.child_cache_elements && item.child_cache_elements.length) {
           flag = checkCacheChildren(item.child_cache_elements);
         }
       }  
     }
   
     return flag;
     
   }

   return checkCacheChildren(this.child_cache_elements);
  
};

/**
 * @method hasParentRole
 *
 * @memberOf OpenAjax.a11y.cache.WidgetElement
 *
 * @desc Tests if a widget has a parent element with a certain role
 *
 * @param {String}  role -  Role to find 
 *
 * @return {Boolean} Returns true if widget has child element with role, otherwise false
 */

OpenAjax.a11y.cache.WidgetElement.prototype.hasParentRole = function (role) {

   function checkParentRole(widget) {
   
     if (!widget) return false;
     
     if (widget.dom_element.role === role) return true;
     
     return checkParentRole(widget.parent_widget);
   
   }
   
   return checkParentRole(this.parent_widget);

};

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.WidgetElement
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.WidgetElement.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.WidgetElement
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style display objects
 */

OpenAjax.a11y.cache.WidgetElement.prototype.getStyle = function () {

  return this.dom_element.getStyle();
  
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.WidgetElement
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of attribute display object
 */

OpenAjax.a11y.cache.WidgetElement.prototype.getAttributes = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  var attributes = this.dom_element.getAttributes();
  
//  cache_nls.addPropertyIfUndefined(attributes, this, 'name');
  
  if (!unsorted) this.dom_element.sortItems(attributes);
  
  return attributes;
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.WidgetElement
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of cache property display object
 */

OpenAjax.a11y.cache.WidgetElement.prototype.getCacheProperties = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;

  var properties = this.dom_element.getCacheProperties(unsorted);

  cache_nls.addPropertyIfDefined(properties, this, 'computed_label');
  cache_nls.addPropertyIfDefined(properties, this, 'computed_label_source');
  cache_nls.addPropertyIfDefined(properties, this, 'computed_label_for_comparison');
  cache_nls.addPropertyIfDefined(properties, this, 'is_widget');
  cache_nls.addPropertyIfDefined(properties, this, 'is_section');
  cache_nls.addPropertyIfDefined(properties, this, 'is_owned');
  cache_nls.addPropertyIfDefined(properties, this, 'owner_controls');
  cache_nls.addPropertyIfDefined(properties, this, 'needs_label');
  
  if (!unsorted) this.dom_element.sortItems(properties);

  return properties;
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.WidgetElement
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.WidgetElement.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};


/**
 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.WidgetElement
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event item display objects
 */

OpenAjax.a11y.cache.WidgetElement.prototype.getEvents = function () {
   
  return this.dom_element.getEvents();
  
};


/**
 * @method getLabelNLS
 *
 * @memberOf OpenAjax.a11y.cache.WidgetElement
 *
 * @desc Returns an object with an NLS localized string and style properties
 *       If label is empty a missing label message will the returned 
 *
 * @return {String | Object} Returns a String if the label has content, 
 *                            but if label is empty it returns an object 
 *                            with a 'label and 'style' property
 */

OpenAjax.a11y.cache.WidgetElement.prototype.getLabelNLS = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  var label_style = {};
  
  if (this.computed_label_length) {
    return this.computed_label;
  }
  else {
    return cache_nls.getNLSMissingLabelMessage();
  }
  
};

/**
 * @method getLabelSourceNLS
 *
 * @memberOf OpenAjax.a11y.cache.WidgetElement
 *
 * @desc Returns an object with an NLS localized information on the source of the label
 *
 * @return {String | Object} Returns a String if the label has content, 
 *                            but if label is empty it returns an object 
 *                            with a 'label and 'style' property
 */

OpenAjax.a11y.cache.WidgetElement.prototype.getLabelSourceNLS = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  return cache_nls.getValueNLS('computed_label_source', this.computed_label_source);
  
};



/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.WidgetElement
 *
 * @desc Returns a text string representation of the input element 
 *
 * @return {String} Returns string represention the InputElement object
 */
 
OpenAjax.a11y.cache.WidgetElement.prototype.toString = function () {
  
  return this.dom_element.tag_name + ": " + this.dom_element.role;
  
};
/*
 * Copyright 2011-2013 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* ---------------------------------------------------------------- */
/*                       DOMElementCache                            */ 
/* ---------------------------------------------------------------- */

/**
 * @constructor DOMElementCache
 *
 * @memberOf OpenAjax.a11y.cache
 * 
 * @desc Creates a DOMElementCache object for represeting a DOM in a web browser
 *         
 * @property {Array}  dom_elements        - A simple array of all the DOMElement objects in the cache
 * @property {Array}  dom_text            - A simple array of all the DOMText objects in the cache
 * @property {Object} page_element        - The dom element that is used as a place to collect results for rules with scope of page
 * @property {Array}  child_dom_elements  - The roor of a tree of DOMElement objects representing the node relationships on the DOM
 * @property {String} sort_property       - String  The DOMElement property the dom_elements array is sorted by
 * @property {Number} length              - The running length of the dom_elements array used for calculating the cache_id property of a DOMElement
 */

OpenAjax.a11y.cache.DOMElementCache = function () {

 this.dom_elements = [];
 this.dom_text = [];
 
 this.child_dom_elements = [];
 
 this.page_element = null;
 
 this.sort_property = 'document_order';
 this.length = 0;
 this.text_length = 0;

};

/** 
 * @method initCache
 *
 * @memberOf OpenAjax.a11y.cache.DOMElementCache
 *
 * @desc Initializes properties of the DOMElementCache
 *
 * @return Nothing
 */

OpenAjax.a11y.cache.DOMElementCache.prototype.initCache = function () {

 this.dom_elements         = [];
 this.child_dom_elements   = [];
 this.sort_property        = 'document_order';
 this.page_element         = null;
 this.length               = 0;
 
};

/** 
 * @method getPageElement
 *
 * @memberOf OpenAjax.a11y.cache.DOMElementCache
 *
 * @desc Gets the DOM node used to contain page level rule results
 *
 * @return {DOMElement} DOM element object used to contain page level rule results
 */

OpenAjax.a11y.cache.DOMElementCache.prototype.getPageElement = function () {

 return this.page_element;
 
};

/**
 * @method addDOMElement
 *
 * @memberOf OpenAjax.a11y.cache.DOMElementCache
 *
 * @desc Adds a DOMElement object to the array of all DOMElements and calculates the elements cache ID
 *
 * @param {DOMElement Object}  dom_element  - DOMElement object to add 
 *
 * @return  {Number}  Returns the current number of elements in the array of DOMElements
 */

OpenAjax.a11y.cache.DOMElementCache.prototype.addDOMElement = function (dom_element) {

  // item must exist and have the position property
  if (dom_element) {   
    this.length = this.length + 1;
    dom_element.document_order = this.length;
    dom_element.cache_id = "element_" + this.length;
    this.dom_elements.push( dom_element );
    
    // only one page element per page
    if ((this.page_element === null) &&
        (dom_element.tag_name === 'body')) {
      this.page_element = dom_element;
    } 
 }

 return this.dom_elements.length;

};

/**
 * @method addDOMText
 *
 * @memberOf OpenAjax.a11y.cache.DOMElementCache
 *
 * @desc Adds a DOM text object to the array of all DOM text and calculates the cache ID
 *
 * @param {DOMText Object}  dom_text  - DOM text object to add 
 *
 * @return  {Number}  Returns the current number of elements in the array of DOM text objects
 */

OpenAjax.a11y.cache.DOMElementCache.prototype.addDOMText = function (dom_text) {

 // item must exist and have the position property
 if (dom_text) {
  this.text_length  += 1;
  dom_text.document_order = this.text_length;
  dom_text.cache_id       = "text_" + this.text_length;
  this.dom_text.push(dom_text);
 }

 return this.dom_text.length;

};

/**
 * @method addChild
 *
 * @memberOf OpenAjax.a11y.cache.DOMElementCache
 *
 * @desc Adds a DOMElement or DOMText object to the root level of the tree reflecting the DOM of document  
 *
 * @param {DOMElement or DOMText object} dom_object  - DOMElement or DOMText object to add to the tree  
 */

OpenAjax.a11y.cache.DOMElementCache.prototype.addChild = function (dom_object) {

  if (dom_object) {
    this.child_dom_elements.push(dom_object);
  }
};

/**
 * @method getDOMElementById
 *
 * @memberOf OpenAjax.a11y.cache.DOMElementCache
 *
 * @desc Returns the DOMElement object with the id attribute value
 *
 * @param {String} id - id of DOMElement object to find
 *
 * @return {DOMElement} Returns DOMElement with the associated id if found, otherwise null
 */
 
OpenAjax.a11y.cache.DOMElementCache.prototype.getDOMElementById = function (id) {

  var i;
  var dom_elements_len = this.dom_elements.length;

  if (id && id.length) {
    for (i=0; i < dom_elements_len; i++) {
      if (this.dom_elements[i].id == id) {
        return this.dom_elements[i];
      }
    } // end loop
  }
  return null;
};

/**
 * @deprecated getDOMElementByCacheId
 *
 * @memberOf OpenAjax.a11y.cache.DOMElementCache
 *
 * @desc Finds the the DOMElement object with the matching cache ID value
 *
 * @param {String} cache_id  - cache_id of DOMElement object to find
 *
 * @return {DOMElement} Returns DOMElement with the associated cache ID if found, otherwise null
 */

OpenAjax.a11y.cache.DOMElementCache.prototype.getDOMElementByCacheId = function (cache_id) {
  return this.getItemByCacheId(cache_id);
};

/**
 * @method getItemByCacheId
 *
 * @memberOf OpenAjax.a11y.cache.DOMElementCache
 *
 * @desc Finds the the DOMElement object with the matching cache ID value
 *
 * @param {String} cache_id  - cache_id of DOMElement object to find
 *
 * @return {DOMElement} Returns DOMElement with the associated cache ID if found, otherwise null
 */

OpenAjax.a11y.cache.DOMElementCache.prototype.getItemByCacheId = function (cache_id) {

  var i;
  var dom_elements_len = this.dom_elements.length;

  if (cache_id && cache_id.length) {
    for (i=0; i < dom_elements_len; i++) {
      if (this.dom_elements[i].cache_id == cache_id) {
        return this.dom_elements[i];
      }
    } // end loop
  }
  return null;
};


/**
 * @method getItemsByNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.DOMElementCache
 *
 * @desc Returns an array of cache items with node results based on the filter 
 *
 * @param  {Number}  filter  - Filter for returning items with node results of a 
 *                             particular type(s)  
 *
 * @return {Array} Returns array of cache items, can be empty
 */

OpenAjax.a11y.cache.DOMElementCache.prototype.getItemsByNodeResults = function (filter, all_flag) {

  return OpenAjax.a11y.util.getItemsByNodeResults(this.dom_elements, filter, all_flag);

};

/**
 * @method sortDOMElements
 *
 *
 * @memberOf OpenAjax.a11y.cache.DOMElementCache
 *
 * @desc Sorts the dom_elements array based on a property of the DOMElement object
 *
 * @param  {String}  property  - DOMElement object property used to sort the array
 * @param  {Boolean} ascending - Boolean  true if sort in ascending order; false in descending order
 *
 * @return  {Boolean}  true if list was sorted, false if not
 */

OpenAjax.a11y.cache.DOMElementCache.prototype.sortDOMElements = function(property, ascending ) {

  var swapped = false;
  var temp = null;
  var i;

  // if the property does not exist or if the cache is empty return false
  if( this.dom_elements &&
    this.dom_elements.length &&
    !this.dom_elements[0][property] ) {
    return false;
  }

  var dom_elements_len = this.dom_elements.length;

  if( ascending ) {
    do{
      swapped = false;
      for (i = 1; i < dom_elements_len; i++ ) {
        if (this.dom_elements[i-1][property] > this.dom_elements[i][property]) {
          // swap the values
          temp = this.dom_elements[i-1];
          this.dom_elements[i-1] = this.dom_elements[i];
          this.dom_elements[i] = temp;
          swapped = true;
        }
      } // end loop
    } while (swapped);
  }
  else {
    do {
      swapped = false;
      for (i = 1; i < dom_elements_len; i++) {
        if (this.dom_elements[i-1][property] < this.dom_elements[i][property]) {
          // swap the values
          temp = this.dom_elements[i-1];
          this.dom_elements[i-1] = this.dom_elements[i];
          this.dom_elements[i] = temp;
          swapped = true;
        }
      } // end loop
    } while (swapped);
  }

  this.sort_property = property;

  return true;
};

/**
 * @method getTextFromIds
 *
 * @memberOf OpenAjax.a11y.cache.DOMElementCache
 *
 * @desc    Gets the accessible text content from a list of ids
 *
 * @note    Used in calculating accessible names, labels and descriptions
 *
 * @param   {String} ids - a space separated list of ids
 *
 * @return  {String} Returns a string with the concatenated text content of the elements with ids  
 */

OpenAjax.a11y.cache.DOMElementCache.prototype.getTextFromIds = function( ids ) {

  var i;
  var text_array = [];
  var id_array = ids.split(' ');
  var id_array_len = id_array.length;
  var dom_element = null;

  for (i=0; i<id_array_len; i++) {
    dom_element = this.getDOMElementById(id_array[i]);
    if (dom_element) {
      text_array.push(dom_element.getText());
    }
  } // end loop

  return text_array.join(' ');
};

/**
 * @method checkForUniqueIDs
 *
 * @memberOf OpenAjax.a11y.cache.DOMElementCache
 *
 * @desc Check DOMElements for unique ids and set id_unique property for all DOMElements in the cache
 *       Sets the 'id_unique' property on DOMElement objects that do not have unique ID attribute values
 */

OpenAjax.a11y.cache.DOMElementCache.prototype.checkForUniqueIDs = function () {

  var i;
  var j;
 
  var dom_elements = this.dom_elements;
  var dom_elements_len1 = dom_elements.length-1;
  var dom_elements_len2 = dom_elements.length;
 
  var de1;
  var de2;

  for (i = 0; i < dom_elements_len1; i++ ) {
    de1 = dom_elements[i];

    for (j=i+1; j < dom_elements_len2; j++) {
      de2 = dom_elements[j];

      if(de1.id.length && de2.id.length && de1.id == de2.id) {
        de1.id_unique = OpenAjax.a11y.ID.NOT_UNIQUE;
        de2.id_unique = OpenAjax.a11y.ID.NOT_UNIQUE;
      }      
    }  
  }
};


/* ---------------------------------------------------------------- */
/*                       DOMText Object                             */ 
/* ---------------------------------------------------------------- */

/** 
 * @constructor DOMText
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc DOMText object represents DOM nodes of type text
 * 
 *
 * @param  {Object}      node           - The DOM text node 
 * @param  {DOMElement}  parent_element - DOMElement object that is the current parent in the tree
 *
 * @property  {DOMElement}  parent_landmark      - LandmarkElement object that contains this element
 *
 * @property  {LandmarkElement}  parent_landmark - LandmarkElement object that contains the text content
 *
 * @property {Number}  type - Type of DOM node element or text
 * @property {String}  text - Text content of DOM text node
 *
 * @property {String}   text_normalized         - Normalized text in the node
 * @property {Number}   text_length             - length of the normalized text in the node 
 * 
 * @property {String}  cache_id        - String that uniquely identifies the cache element in the DOMCache
 * @property {Number}  document_order  - The ordinal position of this DOM text node in the DOM
 *
 * @property {Object}  computed_style  - Object that contains information about run time styling of the node
 * @property {Object}  events          - Object that contains information about event handlers attached to the node and its descendents
 *
 * @property {Boolean}  has_rule_results       - Boolean indicating if the node has any rule results
 * @property {Array}    rules_violations       - Array of NodeResult objects with severity of 'Violation'
 * @property {Array}    rules_manual_checks    - Array of NodeResult objects with severity of 'Manual Check'
 * @property {Array}    rules_warnings         - Array of NodeResult objects with severity of 'Warning'
 * @property {Array}    rules_passed           - Array of NodeResult objects with severity of 'Passed'
 * @property {Array}    rules_hidden           - Array of NodeResult objects with severity of 'Hidden'
 */
 
OpenAjax.a11y.cache.DOMText = function (node, parent_element) {

  this.type = Node.TEXT_NODE;
  this.text = node.data;
  this.parent_element = parent_element;
 
  this.parent_landmark      = null;
 
  this.text_normalized = this.text.normalizeSpace();
  var text_length      = this.text_normalized.length;
  this.text_length     = text_length; 
  
  parent_element.addToCharacterCount(text_length);
 
  this.computed_style = parent_element.computed_style;
 
  // Create areas to store rule results associates with this node
  this.has_rule_results = false;
  this.rules_violations                = [];
  this.rules_manual_checks             = [];
  this.rules_warnings                  = [];
  this.rules_passed                    = [];
  this.rules_hidden                    = [];
};

/**
 * @method addText
 *
 *
 * @memberOf OpenAjax.a11y.cache.DOMText
 *
 * @desc   Check DOMElement for presence of attribute with specified value
 *
 * @param  {String} text  - text content to add
 *
 * @return {Number}  Length of the normailized text content of the DOM text node
 */

OpenAjax.a11y.cache.DOMText.prototype.addText = function (text) {

  this.text += text;
  
  this.text_normalized = this.text.normalizeSpace();
  
  var text_length = this.text_normalized.length;

  this.parent_element.addToCharacterCount(text_length - this.text_length);

  this.text_length = text_length;
  
};


/**
 * @method getId
 *
 * @memberOf OpenAjax.a11y.cache.DOMText
 *
 * @desc   If defined, return the id of the dom node containing this text
 *
 * @return {String} If defined return id value, else empty string
 */

OpenAjax.a11y.cache.DOMText.prototype.getId = function () {

  return this.parent_element.getId();

};

/**
 * @method getClassName
 *
 * @memberOf OpenAjax.a11y.cache.DOMText
 *
 * @desc   If defined, return the class attribute value of the dom node containing this text
 *
 * @return {String} If defined return class value value, else empty string
 */

OpenAjax.a11y.cache.DOMText.prototype.getClassName = function () {

  return this.parent_element.getClassName();

};

/**
 * @method getParentLandmark
 *
 * @memberOf OpenAjax.a11y.cache.DOMText
 *
 * @desc   If defined, return the parent landmark element information
 *
 * @return {String} If defined return class value value, else empty string
 */

OpenAjax.a11y.cache.DOMText.prototype.getParentLandmark = function () {

  return this.parent_element.getParentLandmark();

};

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.DOMText
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.DOMText.prototype.getNodeResults = function () {
 
  function addResultNodes(items) {
  
    var len = items.length;
    
    for (var i = 0; i < len; i++ ) {
      result_nodes.push(items[i]);
    }
    
  }

  var result_nodes = [];
  
  addResultNodes(this.rules_violations);
  addResultNodes(this.rules_manual_checks);
  addResultNodes(this.rules_warnings);
  addResultNodes(this.rules_passed);
  addResultNodes(this.rules_hidden); 
  
  return result_nodes;
  
};

/**
 * @method getAccessibility
 *
 * @memberOf OpenAjax.a11y.cache.DOMText
 *
 * @desc Returns the worst severity level of rule results  
 *
 * @return {Object} Results an object wiith two properties: 'severity' : nls value of the severity, 'style' : a severity styling constant
 */

OpenAjax.a11y.cache.DOMText.prototype.getAccessibility = function () {
   
  var cache_nls      = OpenAjax.a11y.cache_nls;
  var RESULT_VALUE       = OpenAjax.a11y.RESULT_VALUE;

  var severity = cache_nls.getResultValueNLS(RESULT_VALUE.NONE); 

  if (this.rules_hidden.length) {
    severity = cache_nls.getResultValueNLS(RESULT_VALUE.HIDDEN);
  }

  if (this.rules_passed.length) {
    severity = cache_nls.getResultValueNLS(RESULT_VALUE.PASS);
  }

  if (this.rules_manual_checks.length) {
    severity = cache_nls.getResultValueNLS(RESULT_VALUE.MANUAL_CHECK);
  }

  if (this.rules_warnings.length) {
    severity = cache_nls.getResultValueNLS(RESULT_VALUE.WARNING);
  }
  
  if (this.rules_violations.length) {
    severity = cache_nls.getResultValueNLS(RESULT_VALUE.VIOLATION);
  }
  
  return severity;
  
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.DOMText
 *
 * @desc Returns an empty array, text nodes do not have attributes
 *
 * @return {Array} Returns a empty array
 */

OpenAjax.a11y.cache.DOMText.prototype.getAttributes = function (unsorted) {

  return [];

};

/**
 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.DOMText
 *
 * @desc Returns an empty array, text nodes do not have events
 *
 * @return {Array} Returns a empty array
 */

OpenAjax.a11y.cache.DOMText.prototype.getEvents = function (unsorted) {

  return [];

};


/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.DOMText
 *
 * @desc Returns an array of styling information for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of NLS objects for styling
 */

OpenAjax.a11y.cache.DOMText.prototype.getCacheProperties = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;
 
  var properties  = [];

  cache_nls.addPropertyIfDefined(properties, this, 'text_normalized');
  cache_nls.addPropertyIfDefined(properties, this, 'text_length');
  cache_nls.addPropertyIfDefined(properties, this, 'has_rule_results');
  
  cache_nls.addPropertyIfDefined(properties, this, 'parent_landmark');
  
  return properties;

};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.DOMText
 *
 * @desc Returns an array of styling information for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of NLS objects for styling
 */

OpenAjax.a11y.cache.DOMText.prototype.getStyle = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;
 
  var properties  = [];

  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'is_visible_onscreen');
  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'is_visible_to_at');

  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'display');
  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'visibility');
  
  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'color');
  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'opacity');
  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'background_color');
  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'background_image');
  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'background_repeat');
  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'background_position');

  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'font_family');
  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'font_size');
  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'font_weight');

  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'position');
  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'left');
  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'top');
  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'width');
  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'height');
  
  return properties;

};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.DOMText
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number | Object} Returns the value of the property
 */

OpenAjax.a11y.cache.DOMText.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    if (typeof this.computed_style[property] == 'undefined') {
      if (typeof this.parent_element[property] == 'undefined') {
        if (this.parent_landmark) {
          if (typeof this.parent_landmark[property] == 'undefined') {
            if (typeof this.parent_landmark.dom_element[property] == 'undefined') {
              return null;
            }
            else {
              return this.parent_landmark.dom_element[property];
            }
          }
          else {  
            return this.parent_landmark[property];
          }  
        }  
      }  
      else {  
        return this.parent_element[property];
      }  
    }
    else {
      return this.computed_style[property];
    }  
  }
    
  return this[property];
};


/**
 * @method getColorContrastNodeResult
 *
 * @memberOf OpenAjax.a11y.cache.DOMText
 *
 * @desc Returns a node result for a color contrast rule
 *
 * @return {Object} Returns node result object of a color contrast rule
 */
 
OpenAjax.a11y.cache.DOMText.prototype.getColorContrastNodeResult = function() {

  function findColorContrastRule(node_results) {
   
    var node_results_len = node_results.length;
    
    for (var i = 0; i < node_results_len; i++ ) {
    
      var node_result = node_results[i];
      
      var rule_id = node_result.getRuleId();
      
      if (rule_id === "COLOR_1") return node_result;      
      if (rule_id === "COLOR_2") return node_result;
    
    }
  
    return null;
  }

  var nr = findColorContrastRule(this.rules_violations);
  if (nr) return nr;

  nr = findColorContrastRule(this.rules_manual_checks);
  if (nr) return nr;
  
  nr = findColorContrastRule(this.rules_warnings);
  if (nr) return nr;
  
  nr = findColorContrastRule(this.rules_passed);
  if (nr) return nr;
  
  nr = findColorContrastRule(this.rules_hidden);
  if (nr) return nr;
  
  return null;

};

/**
 * @method getText
 *
 * @memberOf OpenAjax.a11y.cache.DOMText
 *
 * @desc Returns text content of a DOMText element
 *
 * @return {String} Returns the text content dom text node
 */
 
OpenAjax.a11y.cache.DOMText.prototype.getText = function() {
  return this.text_normalized;
};

/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.DOMText
 *
 * @desc Returns text representation of a DOMText element
 *
 * @return {String} Returns a string representing the DOM text node
 */
 
OpenAjax.a11y.cache.DOMText.prototype.toString = function(option) {
  var str;
  
  if (option == 'text') str = "'" +  this.text_normalized + "'";
  else str = this.parent_element.tag_name + ": '" +  this.text_normalized + "'";
  
  return str;
};


/* ---------------------------------------------------------------- */
/*                       DOMElement Object                          */ 
/* ---------------------------------------------------------------- */

/** 
 * @constructor DOMElement
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc The DOMElement object represents a dom node of a document
 *
 * @param {Object} node                     - DOM node object of the element
 * @param {Object} parent_element           - DOMElement object that is the parent element of this node
 * 
 * @property  {String}    cache_id            - String that uniquely identifies the cache element in the DOMCache
 * @property  {String}    xpath               - String that identifies the position of the element in the document
 *
 * @property {Array}      child_dom_elements  - The child DOMElement and DOMText objects of this DOMElement in the tree
 * @property {DOMElement} parent_element      - The parent DOMElement of this DOMElement in the tree
 *
 * @property {LandmarkElement}  parent_landmark - LandmarkElement object that contains this element
 *
 * @property {Number}     type                - Type of DOM node is element  
 * @property {Number}     document_order      - The ordinal position of this DOM element node in the DOM
 * 
 * @property {Object}     node                - Reference to the 'live' DOM element represented by this object
 * @property {String}     tag_name            - Tag name of the HTML element in lowercase characters (i.e. p, div, h1, span ...)
 * @property {Array}      aria_attributes     - Array of ARIA properties and states defined for the node
 *
 * @property {String}     id                  - id attribute value of the DOM node (can be empty)
 * @property {Number}     id_unique           - Indicates if id is defined, unique or has a duplicate in the document
 *
 * @property {Number}     character_count     - Count of text charcters in the immediate child DOM text nodes
 * 
 * @property {String}     class_name          - The value of the class attribute of the DOM node
 * @property {String}     role                - The value of the role attribute of the DOM node
 *
 * @property {String}     alt                 - String   The value of the alt attribute of the DOM node
 * @property {Boolean}    has_alt   - true if the alt attribute is defined, otherwise false 
 *
 * @property {String}     title               - The value of the title            attribute of the DOM node
 * @property {String}     aria_describedby    - The value of the aria-describedby attribute of the DOM node
 * @property {String}     aria_hidden         - The value of the aria-hidden      attribute of the DOM node
 * @property {String}     aria_label          - The value of the aria-label       attribute of the DOM node
 * @property {String}     aria_labelledby     - The value of the aria-labelledby  attribute of the DOM node
 * 
 * @property {Boolean}     has_aria_activedescendant     - True if the current element has defined aria-activedescendent attribute, otherwise false
 * @property {Boolean}     ancestor_has_aria_activedescendant - True if a ancestor element has defined aria-activedescendent attribute, otherwise false
 *
 * @property {String}     calculated_aria_description  - If aria-describedby defined this is a string of the 
 *                                                       description content 
 *
 * @property {Boolean}    has_role            - True if element has a role value, otherwise false
 * @property {Boolean}    has_aria_owns            - True if element has a aria-owns property, otherwise false
 * @property {Boolean}    has_aria-attributes - True if element has a aria attributes, otherwise false
 * @property {Boolean}    is_widget           - True if element is a ARIA widget, otherwise false
 * @property {Boolean}    is_landmark         - True if element is a ARIA landmark, otherwise false
 * @property {Boolean}    is_live             - True if element is a ARIA live region, otherwise false
 * @property {Boolean}    is_section          - True if element is a section role, otherwise false
 * @property {Array}      aria_attributes     - Array of ARIA property and state attributes (i.e. attributes 
 *                                              beginning with 'aria-')
 * @property {Array}      other_attributes    - Array of other attribute names and values found on the element 
 *                                              not sotred in another property of this object
 *
 * @property {Array}      invalid_aria_attributes             - Array of attributes that start with aria-, but are not defined by aria
 * @property {Array}      aria_attributes_with_invlaid_values - Array of attributes who have 
 *
 *
 * @property {Object}     role_info         - Object containing information about a widget
 *
 * @property {Object}     events              - Object that contains information about events associated with the node
 * @property {Object}     computed_style      - Object that contains information about run time styling of the node
 *
 * @property {Boolean}    has_rule_results       - Boolean indicating if the node has any rule results
 * @property {Array}      rules_passed           - Array of NodeResult objects with severity of 'Passed'
 * @property {Array}      rules_violations       - Array of NodeResult objects with severity of 'Violation'
 * @property {Array}      rules_manual_checks    - Array of NodeResult objects with severity of 'Manual Check'
 * @property {Array}      rules_warnings         - Array of NodeResult objects with severity of 'Warning'
 * @property {Array}      rules_hidden           - Array of NodeResult objects with severity of 'Hidden'
 *
 * @param {DOM node Object}    node            - The DOM text node 
 * @param {DOMElement Object}  parent_element  - DOMElement object that is the parent DOMElement object in the tree
 */

OpenAjax.a11y.cache.DOMElement = function (node, parent_dom_element) {

  function addAriaAttribute (name, value) {
  
    function getTokens(list) {
    
      var str = "";
      var last = list.length - 1;
      
      for (var i = 0; i < list.length; i++) {
         str += list[i];
         if (i !== last) str += " | ";
      }
    
      return str;
    }
  
    function validValue(value, type, values) {
     
      var i;
      var j;
     
      switch (type) {
       
      case 'boolean':
        if (value === 'true' || value === 'false') return true;
        break;
         
      case 'decimal':
        if (typeof parseFloat(value) === 'number') return true;
        return true;
        break;
         
      case 'idref':
        if (value.length) return true;
        break;
         
      case 'idrefs':
        if (value.length) return true;
        break;
         
      case 'integer':
        if (typeof parseInt(value, 10) === 'number') return true;
        break;
         
      case 'nmtoken':
        for (i = 0; i < values.length; i++) {
          if (value === values[i]) return true; 
        }  
        break;
         
      case 'nmtokens':
        var tokens = [];
        tokens.push(value);
        if (value.indexOf(' ') > 0) tokens = value.split(' ');
        
        var flag = true;
         
        for (i = 0; i < tokens.length && flag; i++) {
          flag = false;
          for (j = 0; j < values.length && !flag; j++) {
            if (tokens[i] === values[j]) flag = true;
          }  
        }
        return flag;
        break;
         
      case 'string':
        if (value.length) return true;
        break;
       
      default:
        break;
       
      }
       
      return false;

    }   // end addAriaAttribute function
    

    var property_info = OpenAjax.a11y.aria.propertyDataTypes[name];

    var av = {};
    av.name = attr.name;
    av.value = attr.value;
    av.type = "undefined";
    av.is_valid_attribute = true;
    av.is_value_valid     = false;
    av.tokens = null;
     
    if (property_info) {
      av.type = property_info.type;
      if (property_info.values) av.tokens = getTokens(property_info.values);
      if (property_info.type === 'boolean') av.tokens = "true | false";
      
      if (typeof property_info.values !== 'undefined') av.is_value_valid = validValue(av.value, av.type, property_info.values);
      else av.is_value_valid = validValue(av.value, av.type, []);
    }
    else {
      av.is_valid_attribute = false;
      invalid_aria_attributes.push(av);
    }
    aria_attributes.push(av);
  }
  
  
  function addOtherAttribute (name, value) {
  
    var oa = {};
    oa.name  = name;
    oa.value = "";
  
    if (typeof value !== 'undefined') {
      oa.value = value;
      other_attributes.push(oa);
    }
    else {
      if ((name === 'required') ||
          (name === 'readonly') ||
          (name === 'disabled') ||
          (name === 'multiple')) {
        other_attributes.push(oa);        
      }    
    }
  }
   

  var i;
  var attr;
  var attributes;
  var attributes_len;  
  var role_info;

  // check to make sure it is a valid node
  if (node === null) return null;

  this.has_element_children = false;
 
  this.type           = Node.ELEMENT_NODE;
  this.document_order = 0;
  this.node           = node;
  this.tag_name       = node.tagName.toLowerCase();
  this.id             = node.id;
 
  if (!this.id || this.id.length === 0) {
    this.id_unique  = OpenAjax.a11y.ID.NOT_DEFINED;
  }
  else {
    this.id_unique  = OpenAjax.a11y.ID.UNIQUE;  
  }
 
  this.character_count = 0;

  // Save relationships with other elements
  this.parent_element = parent_dom_element;
  this.child_dom_elements = [];
  
  var aria_attributes = [];
  var invalid_aria_attributes = []; 
  var other_attributes = [];
  
  this.parent_landmark = null;

  // Cache important attributes for accessibility
  i = 0;
  attr = null;
  attributes = node.attributes;
  attributes_len = attributes.length;

  this.className = "";

//  this.tab_index = node.tabIndex;
  this.tab_index = node.getAttribute('tabindex');
    
  this.draggable = undefined;

  this.is_widget       = false;
  this.is_implied_role = false;
  this.is_interactive  = false;
  this.is_landmark     = false;
  this.is_live         = false;
  this.is_section      = false;
  this.is_abstract     = false;
  this.is_presentation = false;

  this.has_alt                   = false;
  this.has_aria_attributes       = false;
  this.has_aria_activedescendant = false;
  this.has_aria_controls         = false;
  this.has_aria_describedby      = false;
  this.has_aria_flowto           = false;
  this.has_aria_hidden           = false;
  this.has_aria_invalid          = false;
  this.has_aria_label            = false;
  this.has_aria_labelledby       = false;
  this.has_aria_live             = false;
  this.has_aria_owns             = false;  
  this.has_aria_required         = false;  
  this.has_autofocus             = false;
  this.has_headers               = false;
  this.has_href                  = false;
  this.has_lang                  = false;
  this.has_longdesc              = false;
  this.has_pattern               = false;
  this.has_placeholder           = false;
  this.has_required              = false;
  this.has_role                  = false;  
  this.has_scope                 = false;
  this.has_summary               = false;
  this.has_title                 = false;

  this.role_info = null;    
  this.aria_invalid = false;

  this.ancestor_has_aria_activedescendant = false;
  if (parent_dom_element) this.ancestor_has_aria_activedescendant = parent_dom_element.ancestor_has_aria_activedescendant;

  for (i = 0; i < attributes.length; i++) {

    attr = attributes[i];
    
    var attr_value = attr.value.normalizeSpace();

    switch (attr.name) {

    case 'alt':
      this.alt = attr.value;
      this.has_alt = true;
      addOtherAttribute(attr.name, attr_value);
      break;

    case 'aria-activedescendant':
      this.has_aria_activedescendant = true;
      addAriaAttribute('aria-activedescendant', attr_value);
      break;
       
    case 'aria-controls':
      this.has_aria_controls  = true;
      addAriaAttribute('aria-controls', attr_value);
      break;

    case 'aria-describedby':
      this.has_aria_describedby = true;
      this.aria_describedby = attr_value;
      addAriaAttribute('aria-describedby', attr_value);
      this.has_aria_attributes = true;
      break;

    case 'aria-flowto':
      this.has_aria_flowto  = true;
      addAriaAttribute('aria-flowto', attr_value);
      break;

    case 'aria-hidden':
      this.aria_hidden = attr_value.toLowerCase();
      addAriaAttribute('aria-hidden', attr_value);
      this.has_aria_attributes = true;
      this.has_aria_hidden = true;
      break;

    case 'aria-invalid':
      this.aria_invalid = attr_value.toLowerCase() === 'true';
      addAriaAttribute('aria-invalid', attr_value);
      this.has_aria_invalid = true;
      break;

    case 'aria-label':
      this.aria_label = attr_value;
      addAriaAttribute('aria-label', attr_value);
      this.has_aria_label      = true;
      this.has_aria_attributes = true;
      break;

    case 'aria-labelledby':
      this.aria_labelledby  = attr_value;
      addAriaAttribute('aria-labelledby', attr_value);
      this.has_aria_labelledby = true;
      this.has_aria_attributes = true;
      break;

    case 'aria-live':
      this.is_live  = true;
      this.has_aria_live = true;
      addAriaAttribute('aria-live', attr_value);
      this.has_aria_attributes = true;
      break;

    case 'aria-owns':
      this.has_aria_owns  = true;
      this.aria_owns = attr_value;
      addAriaAttribute('aria-owns', attr_value);
      break;

    case 'aria-required':
      this.aria_required = attr_value.toLowerCase() === 'true';
      addAriaAttribute('aria-invalid', attr_value);
      this.has_aria_required = true;
      break;
      
    case 'autofocus':
      this.has_autofocus  = true;
      break;      

    case 'class':
      this.class_name = attr_value.toLowerCase();
      addOtherAttribute(attr.name, attr_value);
      break;

    case 'draggable':
      this.draggable = attr_value.toLowerCase();
      addOtherAttribute(attr.name, attr_value);
      break;

    case 'headers':
      if (attr_value.length > 0) this.has_headers = true;
      break;
      
    case 'href':
      this.has_href = true;
      addOtherAttribute(attr.name, attr_value);
      break;
      
    case 'lang':
      this.lang = attr_value;
      this.has_lang = true;
      addOtherAttribute(attr.name, attr_value);
      break;

    case 'longdesc':
      this.has_longdesc = true;
      break;

    case 'pattern':
      if (attr_value.length > 0) this.has_pattern = true;
      break;

    case 'placeholder':
      if (attr_value.length > 0) this.has_placeholder = true;
      break;
      
    case 'required':
      this.has_required = true;
      break;
      

    case 'role':

      var role = attr_value.toLowerCase();

      this.has_role = true;
      this.role = role;
    
      role_info = OpenAjax.a11y.aria.getRoleObject(role);
      
      if (!role_info || !role_info.roleType) continue;

      this.role_info = role_info;

      switch (role_info.roleType) {
    
      case 'widget':
        this.is_interactive = true;
        this.is_widget = true;
        this.has_range = role_info.hasRange;
        this.is_tab_stoppable = true;
        if (role_info.container && role_info.container.length) this.is_tab_stoppable = false;
        break;
      
      case 'landmark':
        this.is_landmark = true;
        break;
      
      case 'live':
        this.is_live = true;
        break;
      
      case 'abstract':
        this.is_abstract  = true;
        break;
      
      case 'section':
        this.is_section  = true;
        break;
      
      case 'presentation':
        this.is_presentation = true;
        break;
      
      default:
        break;
    
      } // end switch

      break;

    case 'scope':
      if (attr_value.length > 0) this.has_scope = true;
      break;
      
    case 'summary':
      if (attr_value.length > 0) this.has_summary = true;
      break;
      
    case 'title':
      this.title = attr_value;
      if (attr_value.length > 0) this.has_title = true;
      break;
      
    default:

      if (attr.name.indexOf('aria-') === 0 ) {
        addAriaAttribute(attr.name, attr_value);
        this.has_aria_attributes = true;
      }
      else {
        addOtherAttribute(attr.name, attr_value);
      }
      break;

    } // end switch
  } // end loop0


  this.aria_attributes          = aria_attributes;
  this.other_attributes         = other_attributes;
  this.invalid_aria_attributes  = invalid_aria_attributes;

  this.events = {};
  this.events.supports_events = false;
  
  switch (OpenAjax.a11y.EVENT_HANDLER_PROCESSOR) {
  
  case 'firefox':
    this.events = this.EnumerateFirefoxEvents(node, parent_dom_element);
    break;

  case 'fae-util':
    this.events = this.EnumerateFaeUtilEvents(node, parent_dom_element);
    break;

  default:
    break;
  }


  // Create areas to store rule results associates with this node
  this.has_rule_results = false;
  this.rules_violations                = [];
  this.rules_manual_checks             = [];
  this.rules_warnings                  = [];
  this.rules_passed                    = [];
  this.rules_hidden                    = [];

  return this;

};

/**
 * @method setImpliedRole
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc  Sets an implid role for the DOM element
 *
 * @param  {String} role - new role for the element
 */
 
OpenAjax.a11y.cache.DOMElement.prototype.setImpliedRole = function (role) {

  if (!this.has_role && typeof role === 'string' && (role.length > 0)) {

    role_info = OpenAjax.a11y.aria.getRoleObject(role);
    
    if (!role_info) return;

    this.has_role = true;
    this.role = role;
    this.is_implied_role = true;
    
    this.role_info = role_info;

    if (role_info && role_info.roleType) {
      
      switch (role_info.roleType) {
    
      case 'widget':
        this.is_widget = true;
        break;
      
      case 'landmark':
        this.is_landmark = true;
        break;
      
      case 'live':
        this.is_live = true;
        break;
      
      case 'abstract':
        this.is_abstract  = true;
        break;
      
      case 'section':
        this.is_section  = true;
        break;
      
      default:
        break;
   
      } // end switch
    }  
  }
};

/**
 * @method getId
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc   If definded, return the id of the dom node
 *
 * @return {String} If defined return id value, else empty string
 */

OpenAjax.a11y.cache.DOMElement.prototype.getId = function () {

  if (this.id) return this.id;
  
  return "";

};

/**
 * @method getRole
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc   If definded, return the role 
 *
 * @return {String} If defined return role value, else empty string
 */

OpenAjax.a11y.cache.DOMElement.prototype.getRole = function () {

  if (this.role) return this.role;
 
  return "";

};

/**
 * @method getClassName
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc   If defined, return the class attribute value of the dom node
 *
 * @return {String} If defined return class value value, else empty string
 */

OpenAjax.a11y.cache.DOMElement.prototype.getClassName = function () {

  if (this.class_name) return this.class_name;
  
  return "";

};


/**
 * @method getParentLandmark
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc   If defined, return the parent landmark element information
 *
 * @return {String} If defined return class value value, else empty string
 */

OpenAjax.a11y.cache.DOMElement.prototype.getParentLandmark = function () {

  if (this.parent_landmark) return this.parent_landmark.toString();
  
  return "none";

};


/**
 * @method hasAttrWithValue
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc   Check DOMElement for presence of attribute with specified value
 *
 * @param  {String} name  - name of attribute
 * @param  {String} value - value of attribute
 *
 * @return {boolean} Indicates whether or not DOMElement has the specified
 *                   attribute with the specified value.
 */

OpenAjax.a11y.cache.DOMElement.prototype.hasAttrWithValue = function (name, value) {

  if (this.hasOwnProperty (name)) {
    return this[name] === value;
  }

  return false;

};

/**
 * @method hasOwns
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc   Check DOMElement for presence of aria-owns attribute
 *
 * @return {boolean} Indicates whether or not DOMElement has aria-owns attribute with values
 */

OpenAjax.a11y.cache.DOMElement.prototype.hasOwns = function () {

  return this.has_aria_owns;

};

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.DOMElement.prototype.getNodeResults = function () {
 
  function addResultNodes(items) {
  
    var len = items.length;
    
    for (var i = 0; i < len; i++ ) {
      result_nodes.push(items[i]);
    }
    
  }

  var result_nodes = [];
  
  addResultNodes(this.rules_violations);
  addResultNodes(this.rules_manual_checks);
  addResultNodes(this.rules_warnings);
  addResultNodes(this.rules_passed);
  addResultNodes(this.rules_hidden);
  
  return result_nodes;
  
};

/**
 * @method containsInteractiveElements
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc Tests if a DOM element has any descendants that are widgets 
 *
 * @return {Boolean}  'True' if at least one descendant dom element is a widget, otherwise 'false'
 */

OpenAjax.a11y.cache.DOMElement.prototype.containsInteractiveElements = function () {

  function checkChildren(children) {
  
     if ((typeof children != 'object') || !children.length ) return false;
     
     var flag = false;
     
     for (var i = 0; (i < children.length) && !flag; i++) {
     
       var child = children[i];
     
       if (child.type != Node.ELEMENT_NODE) continue;
     
       if (child.is_interactive) flag = true;
       else if (child.child_dom_elements.length) flag = checkChildren(child.child_dom_elements);
     
     }
     
     return flag;
  
  }

  return checkChildren(this.child_dom_elements);

};


/**
 * @method hasParentRole
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc Tests if a widget has a parent element with a certain role
 *
 * @param {String}  role -  Role to find 
 *
 * @return {Boolean} Returns true if widget has child element with role, otherwise false
 */

OpenAjax.a11y.cache.DOMElement.prototype.hasParentRole = function (role) {

   function checkParentElementForRole(dom_element) {

     if (!dom_element) return false;

     if (dom_element.role === role) {
       return true;
     }
     else {
       return checkParentElementForRole(dom_element.parent_element);     
     }
     
   }

   return checkParentElementForRole(this.parent_element);
  
};

/**
 * @method getHasDescribedBy
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc Returns NLS string on whether the dom element has a aria-describedby attribute  
 *
 * @return {String} If true returns "Yes", else "No"
 */

OpenAjax.a11y.cache.DOMElement.prototype.getHasDescribedBy = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;

  if (this.has_aria_describedby) return cache_nls.getLabelAndValueNLS('has_aria_describedby', this.has_aria_describedby).value;

  return "";
};

/**
 * @method getAccessibility
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc Returns the worst severity level of rule results  
 *
 * @return {Object} Results an object wiith two properties: 'severity' : nls value of the severity, 'style' : a severity styling constant
 */

OpenAjax.a11y.cache.DOMElement.prototype.getAccessibility = function () {
   
  var cache_nls      = OpenAjax.a11y.cache_nls;
  var RESULT_VALUE       = OpenAjax.a11y.RESULT_VALUE;

  var severity = cache_nls.getResultValueNLS(RESULT_VALUE.NONE); 
  a.label    = severity.label;

  if (this.rules_hidden.length) {
    severity = cache_nls.getResultValueNLS(RESULT_VALUE.HIDDEN);
  }

  if (this.rules_passed.length) {
    severity = cache_nls.getResultValueNLS(RESULT_VALUE.PASS);
  }

  if (this.rules_manual_checks.length) {
    severity = cache_nls.getResultValueNLS(RESULT_VALUE.MANUAL_CHECK);
  }

  if (this.rules_warnings.length) {
    severity = cache_nls.getResultValueNLS(RESULT_VALUE.WARNING);
  }
  
  if (this.rules_violations.length) {
    severity = cache_nls.getResultValueNLS(RESULT_VALUE.VIOLATION);
  }

  return severity;
  
};


/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.DOMElement.prototype.getAttributes = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  var i;
  
  var attributes  = [];
  
  for (i = 0; i < this.aria_attributes.length; i++) {
    var aa = this.aria_attributes[i];
    attributes.push(cache_nls.getLabelAndValueNLS(aa.name, aa.value));
  }

  for (i = 0; i < this.other_attributes.length; i++) {
    var oa = this.other_attributes[i];
    attributes.push(cache_nls.getLabelAndValueNLS(oa.name, oa.value));
  }
  
  if (!unsorted) this.sortItems(attributes);
  
  return attributes;

};

/**
 * @method hasEvents
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc Returns if an element has user interface events attached to it
 *
 * @return {Boolean} Returns true if event user interface event handlers are attached to the node, otherwise false
 */

OpenAjax.a11y.cache.DOMElement.prototype.hasEvents = function () {

  var has_events = false;

  has_events = has_events || this.events.has_load;

  has_events = has_events || this.events.has_change;

  has_events = has_events || this.events.has_click;
  has_events = has_events || this.events.has_double_click;

  has_events = has_events || this.events.has_focus;
  has_events = has_events || this.events.has_blur;

  has_events = has_events || this.events.has_key_down;
  has_events = has_events || this.events.has_key_press;
  has_events = has_events || this.events.has_key_up;

  has_events = has_events || this.events.has_mouse_down;
  has_events = has_events || this.events.has_mouse_up;
  has_events = has_events || this.events.has_mouse_move;
  has_events = has_events || this.events.has_mouse_out;
  has_events = has_events || this.events.has_mouse_over;
  has_events = has_events || this.events.has_mouse_enter;
  has_events = has_events || this.events.has_mouse_leave;

  has_events = has_events || this.events.has_drag;
  has_events = has_events || this.events.has_drag_end;
  has_events = has_events || this.events.has_drag_enter;
  has_events = has_events || this.events.has_drag_leave;
  has_events = has_events || this.events.has_drag_over;
  has_events = has_events || this.events.has_drag_start;
  has_events = has_events || this.events.has_drop;
  
  return has_events;
};


/**
 * @method hasChangeEvents
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc Returns true if the element has change events
 *
 * @param  {Array}   prop_list : If defined, the names of the event handlers defined will be added to the list as property object
 * 
 * @return {Boolean} Returns "True" element has change event
 */
 
OpenAjax.a11y.cache.DOMElement.prototype.hasChangeEvents = function (prop_list) {

  function addEvent(event, name) {

    if (event) {
      has_event = true;
      
      if (typeof prop_list === 'object') {
        var prop = {};
      
        prop.label = name;
        prop.value = 'true';
        prop.description = "";
      
        prop_list.push(prop);
      }  
    }   
  }

  var has_event = false;
  
  addEvent(this.events.has_change,  'onchange');

  return has_event;
  
};


/**
 * @method hasFocusEvents
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc Returns true if the element has focus or blur events
 *
 * @param  {Array}   prop_list : If defined, the names of the event handlers defined will be added to the list as property object
 * 
 * @return {Boolean} Returns "True" element has focus and blur events
 */
 
OpenAjax.a11y.cache.DOMElement.prototype.hasFocusEvents = function (prop_list) {

  function addEvent(event, name) {

    if (event) {
      has_event = true;
      
      if (typeof prop_list === 'object') {
        var prop = {};
      
        prop.label = name;
        prop.value = 'true';
        prop.description = "";
      
        prop_list.push(prop);
      }  
    }   
  }

  var has_event = false;
  
  addEvent(this.events.has_focus,  'onfocus');
  addEvent(this.events.has_blur,   'onblur');

  return has_event;
  
};



/**
 * @method hasClickEvents
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc Returns true if the element has click or double click events
 *
 * @param  {Array}   prop_list : If defined, the names of the event handlers defined will be added to the list as property object
 * 
 * @return {Boolean} Returns "True" element has click and double click events
 */
 
OpenAjax.a11y.cache.DOMElement.prototype.hasClickEvents = function (prop_list) {

  function addEvent(event, name) {

    if (event) {
      has_event = true;
      
      if (typeof prop_list === 'object') {
        var prop = {};
      
        prop.label = name;
        prop.value = 'true';
        prop.description = "";
      
        prop_list.push(prop);
      }  
    }   
  }

  var has_event = false;
  
  addEvent(this.events.has_click,  'onclick');
  addEvent(this.events.has_double_click,    'ondoubleclick');

  return has_event;
  
};


/**
 * @method hasMouseEvents
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc Returns true if the element has mouse specfic event handlers (i.e. up, down, move, over, out)
 *
 * @param  {Array}   prop_list : If defined, the names of the event handlers defined will be added to the list as property object
 * 
 * @return {Boolean} Returns "True" element has mouse up, down, move, over and/or out events
 */
 
OpenAjax.a11y.cache.DOMElement.prototype.hasMouseEvents = function (prop_list) {

  function addEvent(event, name) {

    if (event) {
      has_event = true;
      
      if (typeof prop_list === 'object') {
        var prop = {};
      
        prop.label = name;
        prop.value = 'true';
        prop.description = "";
      
        prop_list.push(prop);
      }  
    }   
  }

  var has_event = false;
  
  addEvent(this.events.has_mouse_down,  'onmousedown');
  addEvent(this.events.has_mouse_up,    'onmouseup');
  addEvent(this.events.has_mouse_move,  'onmousemove');
  addEvent(this.events.has_mouse_out,   'onmouseout');
  addEvent(this.events.has_mouse_over,  'onmouseover');
  addEvent(this.events.has_mouse_enter, 'onmouseenter');
  addEvent(this.events.has_mouse_leave, 'onmouseleave');

  return has_event;
  
};

/**
 * @method hasDragEvents
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc Returns true if the element has drag specfic event handlers 
 *
 * @param  {Array}   prop_list : If defined, the names of the event handlers defined will be added to the list as property object
 * 
 * @return {Boolean} Returns true if element has drag events. otherwise false
 */
 
OpenAjax.a11y.cache.DOMElement.prototype.hasDragEvents = function (prop_list) {

  function addEvent(event, name) {
  
//     OpenAjax.a11y.logger.debug("   " + name + "=" + event);

    if (event) {
      has_event = true;
      
      if (typeof prop_list === 'object') {
        var prop = {};
      
        prop.label = name;
        prop.value = 'true';
        prop.description = "";
      
        prop_list.push(prop);
      }  
    }   
  }

  var has_event = false;
  
  var de = this;

//  OpenAjax.a11y.logger.debug("DRAG: " + de.toString());

  addEvent(this.events.has_drag,       'ondrag');
  addEvent(this.events.has_drag_end,   'ondragend');
  addEvent(this.events.has_drag_enter, 'ondragenter');
  addEvent(this.events.has_drag_leave, 'ondragleave');
  addEvent(this.events.has_drag_over,  'ondragover');
  addEvent(this.events.has_drag_start, 'ondragstart');
  addEvent(this.events.has_drop,       'ondrop');

  return has_event;
  
};


/**
 * @method hasKeyboardEvents
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc Returns true if the element has keyboard specfic event handlers 
 *
 * @param  {Array}   prop_list : If defined, the names of the event handlers defined will be added to the list as property object
 * 
 * @return {Boolean} Returns true if element has keyboard events, otherwise false
 */
 
OpenAjax.a11y.cache.DOMElement.prototype.hasKeyboardEvents = function (str, prop_list) {

  function addEvent(event, name) {

    if (event) {
      has_event = true;
      
      if (typeof prop_list === 'object') {
        var prop = {};
      
        prop.label = name;
        prop.value = 'true';
        prop.description = "";
      
        prop_list.push(prop);
      }  
    }   
  }

  var has_event = false;
  
  addEvent(this.events.has_key_down,  'onkeydown');
  addEvent(this.events.has_key_press, 'onkeypress');
  addEvent(this.events.has_key_up,    'onkeyup');

  return has_event;
  
};




/**
 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc Returns an array of objects representing events associated with the element 
 *
 * @return {Array} Returns a array of event object results
 */

OpenAjax.a11y.cache.DOMElement.prototype.getEvents = function () {

  function addHasEvent(event_type, event_on_element, event_on_ancestor) {
  
    var o = {};
    
    o.label = event_type;
    o.event_on_element        = nls_false;
    o.event_on_element_style  = "no";
    o.event_on_ancestor       = nls_false;
    o.event_on_ancestor_style = "no";

    if (event_on_element) {
      o.event_on_element        = nls_true;
      o.event_on_element_style  = "yes";
    }
    
    if (event_on_ancestor) {
      o.event_on_ancestor       = nls_true;
      o.event_on_ancestor_style = "yes";
    }  

    events.push(o);
  
  }

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  var nls_false = cache_nls.getCacheNLS().boolean_values.false_value;
  var nls_true  = 'true';
 
  var events = [];
  
  addHasEvent('blur',          this.events.has_blur,          this.events.ancestor_has_blur);
  addHasEvent('focus',         this.events.has_focus,         this.events.ancestor_has_focus);
  
  addHasEvent('click',         this.events.has_click,         this.events.ancestor_has_click);
  addHasEvent('double click',  this.events.has_double_click,  this.events.ancestor_has_double_click);

  addHasEvent('key down',      this.events.has_key_down,      this.events.ancestor_has_key_down);
  addHasEvent('key press',     this.events.has_key_press,     this.events.ancestor_has_key_press);
  addHasEvent('key down',      this.events.has_key_up,        this.events.ancestor_has_key_up);

  addHasEvent('mouse down',    this.events.has_mouse_down,    this.events.ancestor_has_mouse_down);
  addHasEvent('mouse up',      this.events.has_mouse_up,      this.events.ancestor_has_mouse_up);
  addHasEvent('mouse move',    this.events.has_mouse_move,    this.events.ancestor_has_mouse_move);

  addHasEvent('mouse leave',    this.events.has_mouse_leave,    this.events.ancestor_has_mouse_leave);
  addHasEvent('mouse enter',    this.events.has_mouse_enter,    this.events.ancestor_has_mouse_enter);
  
  addHasEvent('mouse out',     this.events.has_mouse_out,     this.events.ancestor_has_mouse_out);
  addHasEvent('mouse over',    this.events.has_mouse_over,    this.events.ancestor_has_mouse_over);
  
  addHasEvent('drag',        this.events.has_drag,       this.events.ancestor_has_drag);
  addHasEvent('drag end',    this.events.has_drag_end,   this.events.ancestor_has_drag_end);
  addHasEvent('drag enter',  this.events.has_drag_enter, this.events.ancestor_has_drag_enter);
  addHasEvent('drag leave',  this.events.has_drag_leave, this.events.ancestor_has_drag_leave);
  addHasEvent('drag over',   this.events.has_drag_over,  this.events.ancestor_has_drag_over);
  addHasEvent('drag start',  this.events.has_drag_start, this.events.ancestor_has_drag_start);
  addHasEvent('drop',        this.events.has_drop,       this.events.ancestor_has_drag_drop);
  
  addHasEvent('change',        this.events.has_change,        this.events.ancestor_has_change);

  return events;

};


/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc Returns an array of styling information for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of NLS objects for styling
 */

OpenAjax.a11y.cache.DOMElement.prototype.getStyle = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;
 
  var properties  = [];

  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'is_visible_onscreen');
  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'is_visible_to_at');

  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'display');
  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'visibility');
  
  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'color');
  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'opacity');
  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'background_color');
  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'background_image');
  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'background_repeat');
  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'background_position');

  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'font_family');
  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'font_size');
  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'font_weight');

  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'position');
  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'left');
  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'top');
  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'width');
  cache_nls.addPropertyIfDefined(properties, this.computed_style, 'height');
  
  return properties;

};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc Returns an array of styling information for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of NLS objects for styling
 */

OpenAjax.a11y.cache.DOMElement.prototype.getCacheProperties = function () {

  var i;

  var cache_nls = OpenAjax.a11y.cache_nls;
 
  var properties  = [];

  cache_nls.addPropertyIfDefined(properties, this, 'id_unique');
  cache_nls.addPropertyIfDefined(properties, this, 'xpath');
  cache_nls.addPropertyIfDefined(properties, this, 'character_count');
  
  cache_nls.addPropertyIfDefined(properties, this, 'calculated_aria_description');

  cache_nls.addPropertyIfDefined(properties, this, 'document_order');

  cache_nls.addPropertyIfDefined(properties, this, 'parent_landmark');

  cache_nls.addPropertyIfDefined(properties, this, 'is_interactive');
  cache_nls.addPropertyIfDefined(properties, this, 'is_widget');
  cache_nls.addPropertyIfDefined(properties, this, 'is_landmark');
  cache_nls.addPropertyIfDefined(properties, this, 'is_live');
  
  cache_nls.addPropertyIfDefined(properties, this, 'has_rule_results');
  cache_nls.addPropertyIfDefined(properties, this, 'has_role');
  cache_nls.addPropertyIfDefined(properties, this, 'is_implied_role');

  cache_nls.addPropertyIfDefined(properties, this, 'has_aria_activedescendant');
  cache_nls.addPropertyIfDefined(properties, this, 'is_owned');

  var invalid_aria_attributes     = this.invalid_aria_attributes;
  var invalid_aria_attributes_len = invalid_aria_attributes.length;

  for (i = 0; i < invalid_aria_attributes_len; i++) {
    cache_nls.addInvalidAttribute(properties, invalid_aria_attributes[i]);
  }
  
  return properties;

};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number | Object} Returns the value of the property
 */

OpenAjax.a11y.cache.DOMElement.prototype.getCachePropertyValue = function (property) {

//  OpenAjax.a11y.logger.debug("dom element property: " + property + " value= " + this[property]);

  if (typeof this[property] === 'undefined') {
     if(typeof this.computed_style[property] === 'undefined') {
       if(typeof this.events[property] === 'undefined') {
         for (var i = 0; i < this.aria_attributes.length; i++) {
            var attr = this.aria_attributes[i];
            if (attr.name === property) return attr.value;
         }
         return null;
       }
       else {
         return this.events[property]; 
       }   
     }
     else {
       return this.computed_style[property]; 
     }
  }
  
  return this[property];
  
};


/**
 * @method sortItems
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.DOMElement.prototype.sortItems = function (items) {

  var swapped = false;
  var temp = null;
  var i;
  var items_len = items.length;

  do{
    swapped = false;
    for (i = 1; i < items_len; i++ ) {
     if (items[i-1].label.toLowerCase() > items[i].label.toLowerCase()) {
      // swap the values
      temp = items[i-1];
      items[i-1] = items[i];
      items[i] = temp;
      swapped = true;
     }
    } // end loop
  } while (swapped);

};

/** 
 * @method EnumerateFirefoxEvents
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc  Finds the event information of the node for a DOMElement object
 *
 * @param  {Object}     node              - Object The DOM element node that corresponds
 *                                          to this DOMElement object, and from which
 *                                          common information is derived for the DOM
 *                                          element cache.
 *
 * @param  {DOMElement} parent_dom_element - Parent DOMElement object associated with the node's parent node 
 *
 * @return {Object}  Returns an object with event information
 */

OpenAjax.a11y.cache.DOMElement.prototype.EnumerateFirefoxEvents = function (node, parent_dom_element) {

  var i;
 
  var events = {};
  events.supports_events = false;

  var event_listener = Components.classes["@mozilla.org/eventlistenerservice;1"];

  if (event_listener !== null &&
    event_listener !== undefined) {

    events.supports_events = true;

    events.has_blur         = false;
    
    events.has_change       = false;
    
    events.has_click        = false;
    events.has_double_click = false;
    
    events.has_focus        = false;
    
    events.has_key_down     = false;
    events.has_key_press    = false;
    events.has_key_up       = false;
    
    events.has_load         = false;
    
    events.has_mouse_down   = false;
    events.has_mouse_up     = false;
    events.has_mouse_move   = false;
    events.has_mouse_out    = false;
    events.has_mouse_over   = false;
    events.has_mouse_enter  = false;
    events.has_mouse_leave  = false;

    events.has_drag       = false;
    events.has_drag_end   = false;
    events.has_drag_enter = false;
    events.has_drag_leave = false;
    events.has_drag_over  = false;
    events.has_drag_start = false;
    events.has_drop       = false;

    events.has_pointer_up      = false;
    events.has_pointer_cancel  = false;
    events.has_pointer_move    = false;
    events.has_pointer_over    = false;
    events.has_pointer_out     = false;
    events.has_pointer_enter   = false;
    events.has_pointer_leave   = false;

    events.has_touch_start   = false;
    events.has_touch_end     = false;
    events.has_touch_leave   = false;
    events.has_touch_move    = false;
    events.has_touch_cancel  = false;
  
    if (parent_dom_element && parent_dom_element.events) {
      events.ancestor_has_blur         = parent_dom_element.events.has_blur         || parent_dom_element.events.ancestor_has_blur;
      events.ancestor_has_change       = parent_dom_element.events.has_change       || parent_dom_element.events.ancestor_has_change;
      events.ancestor_has_click        = parent_dom_element.events.has_click        || parent_dom_element.events.ancestor_has_click;
      events.ancestor_has_double_click = parent_dom_element.events.has_double_click || parent_dom_element.events.ancestor_has_double_click;
      events.ancestor_has_focus        = parent_dom_element.events.has_focus        || parent_dom_element.events.ancestor_has_focus;
      events.ancestor_has_key_down     = parent_dom_element.events.has_key_down     || parent_dom_element.events.ancestor_has_key_down;
      events.ancestor_has_key_press    = parent_dom_element.events.has_key_press    || parent_dom_element.events.ancestor_has_key_press;
      events.ancestor_has_key_up       = parent_dom_element.events.has_key_up       || parent_dom_element.events.ancestor_has_key_up;
      events.ancestor_has_load         = parent_dom_element.events.has_load         || parent_dom_element.events.ancestor_has_load;
      
      events.ancestor_has_mouse_down   = parent_dom_element.events.has_mouse_down   || parent_dom_element.events.ancestor_has_mouse_down;
      events.ancestor_has_mouse_up     = parent_dom_element.events.has_mouse_up     || parent_dom_element.events.ancestor_has_mouse_up;
      events.ancestor_has_mouse_move   = parent_dom_element.events.has_mouse_move   || parent_dom_element.events.ancestor_has_mouse_move;
      events.ancestor_has_mouse_out    = parent_dom_element.events.has_mouse_out    || parent_dom_element.events.ancestor_has_mouse_out;
      events.ancestor_has_mouse_over   = parent_dom_element.events.has_mouse_over   || parent_dom_element.events.ancestor_has_mouse_over;
      events.ancestor_has_mouse_enter  = parent_dom_element.events.has_mouse_enter  || parent_dom_element.events.ancestor_has_mouse_enter;
      events.ancestor_has_mouse_leave  = parent_dom_element.events.has_mouse_leave  || parent_dom_element.events.ancestor_has_mouse_leave;

      events.ancestor_has_drag       = parent_dom_element.events.has_drag       || parent_dom_element.events.ancestor_has_drag;
      events.ancestor_has_drag_end   = parent_dom_element.events.has_drag_end   || parent_dom_element.events.ancestor_has_drag_end;
      events.ancestor_has_drag_enter = parent_dom_element.events.has_drag_enter || parent_dom_element.events.ancestor_has_drag_enter;
      events.ancestor_has_drag_leave = parent_dom_element.events.has_drag_leave || parent_dom_element.events.ancestor_has_drag_leave;
      events.ancestor_has_drag_over  = parent_dom_element.events.has_drag_over  || parent_dom_element.events.ancestor_has_drag_over;
      events.ancestor_has_drag_start = parent_dom_element.events.has_drag_start || parent_dom_element.events.ancestor_has_drag_start;
      events.ancestor_has_drop       = parent_dom_element.events.has_drop       || parent_dom_element.events.ancestor_has_drop;

      events.ancestor_has_pointer_up      = parent_dom_element.events.has_pointer_up     || parent_dom_element.events.ancestor_has_pointer_up;
      events.ancestor_has_pointer_cancel  = parent_dom_element.events.has_pointer_cancel || parent_dom_element.events.ancestor_has_pointer_cancel;
      events.ancestor_has_pointer_move    = parent_dom_element.events.has_pointer_move   || parent_dom_element.events.ancestor_has_pointer_move;
      events.ancestor_has_pointer_over    = parent_dom_element.events.has_pointer_over   || parent_dom_element.events.ancestor_has_pointer_over;
      events.ancestor_has_pointer_out     = parent_dom_element.events.has_pointer_out    || parent_dom_element.events.ancestor_has_pointer_out;
      events.ancestor_has_pointer_enter   = parent_dom_element.events.has_pointer_enter  || parent_dom_element.events.ancestor_has_pointer_enter;
      events.ancestor_has_pointer_leave   = parent_dom_element.events.has_pointer_leave  || parent_dom_element.events.ancestor_has_pointer_leave;

      events.ancestor_has_touch_start   = parent_dom_element.events.has_touch_start  || parent_dom_element.events.ancestor_has_touch_start;
      events.ancestor_has_touch_end     = parent_dom_element.events.has_touch_end    || parent_dom_element.events.ancestor_has_touch_end;
      events.ancestor_has_touch_leave   = parent_dom_element.events.has_touch_leave  || parent_dom_element.events.ancestor_has_touch_leave;
      events.ancestor_has_touch_move    = parent_dom_element.events.has_touch_move   || parent_dom_element.events.ancestor_has_touch_move;
      events.ancestor_has_touch_cancel  = parent_dom_element.events.has_touch_cancel || parent_dom_element.events.ancestor_has_touch_cancel;
    }
    else {
      events.ancestor_has_blur         = false;
      events.ancestor_has_change       = false;
      events.ancestor_has_click        = false;
      events.ancestor_has_double_click = false;
      events.ancestor_has_focus        = false;
      events.ancestor_has_key_down     = false;
      events.ancestor_has_key_press    = false;
      events.ancestor_has_key_up       = false;
      events.ancestor_has_load         = false;
      
      events.ancestor_has_mouse_down   = false;
      events.ancestor_has_mouse_up     = false;
      events.ancestor_has_mouse_move   = false;
      events.ancestor_has_mouse_out    = false;
      events.ancestor_has_mouse_over   = false;
      events.ancestor_has_mouse_enter  = false;
      events.ancestor_has_mouse_leave  = false;

      events.ancestor_has_drag       = false;
      events.ancestor_has_drag_end   = false;
      events.ancestor_has_drag_enter = false;
      events.ancestor_has_drag_leave = false;
      events.ancestor_has_drag_over  = false;
      events.ancestor_has_drag_start = false;
      events.ancestor_has_drop       = false;

      events.ancestor_has_pointer_up      = false;
      events.ancestor_has_pointer_cancel  = false;
      events.ancestor_has_pointer_move    = false;
      events.ancestor_has_pointer_over    = false;
      events.ancestor_has_pointer_out     = false;
      events.ancestor_has_pointer_enter   = false;
      events.ancestor_has_pointer_leave   = false;

      events.ancestor_has_touch_start   = false;
      events.ancestor_has_touch_end     = false;
      events.ancestor_has_touch_leave   = false;
      events.ancestor_has_touch_move    = false;
      events.ancestor_has_touch_cancel  = false;

   }

   var event_listener_service = event_listener.createInstance(Components.interfaces.nsIEventListenerService);
   var node_event_service     = event_listener_service.getListenerInfoFor(node, {});

   for (i = 0; i < node_event_service.length; i++) {
     var node_event_information = node_event_service[i].QueryInterface(Components.interfaces.nsIEventListenerInfo);
     
//     OpenAjax.a11y.logger.debug("Event Info: type=" + node_event_information.type + " system event=" + node_event_information.inSystemEventGroup + " allows untrusted=" + node_event_information.allowsUntrusted);

       switch (node_event_information.type) {

       case "blur":
         events.has_blur = true;
         break;

       case "change":
         events.has_change = true;
         break;

       case "click":
         events.has_click = true;
         break;

       case "dbclick":
         events.has_double_click = true;
         break;

       case "focus":
         events.has_focus   = true;
         break;

       case "keydown":
         events.has_key_down  = true;
         break;

       case "keypress":
         events.has_key_press = true;
         break;

       case "keyup":
         events.has_key_up   = true;
         break;

       case "load":
         events.has_load    = true;
         break;

       case "mousedown":
         events.has_mouse_down = true;
         break;

       case "mouseup":
         events.has_mouse_up  = true;
         break;

       case "mousemove":
         events.has_mouse_move = true;
         break;

       case "mouseout":
         events.has_mouse_out = true;
         break;

       case "mouseover":
         events.has_mouse_over = true;
         break;

       case "mouseenter":
         events.has_mouse_enter = true;
         break;

       case "mouseleave":
         events.has_mouse_leave = true;
         break;

       case "drag":
         events.has_drag = true;
         break;

       case "dragend":
         events.has_drag_end = true;
         break;

       case "dragenter":
         events.has_drag_enter = true;
         break;
         
       case "dragleave":
         events.has_drag_leave = true;
         break;
         
       case "dragover":
         events.has_drag_over = true;
         break;
         
       case "dragstart":
         events.has_drag_start = true;
         break;
         
       case "drop":
         events.has_drop = true;
         break;

       case "pointerup":
         events.has_pointer_up = true;
         break;

       case "pointer_cancel":
         events.has_pointer_cancel = true;
         break;

       case "pointer_move":
         events.has_pointer_move = true;
         break;

       case "pointer_over":
         events.has_pointer_over = true;
         break;

       case "pointer_out":
         events.has_pointer_out = true;
         break;

       case "pointer_enter":
         events.has_pointer_enter = true;
         break;

       case "pointer_leave":
         events.has_pointer_leave = true;
         break;

       case "touchstart":
         events.has_touch_start = true;
         break;

       case "touchend":
         events.has_touch_end = true;
         break;
         
       case "touchcancel":
         events.has_touch_cancel = true;
         break;
         
       case "touchleave":
         events.has_touch_leave = true;
         break;
         
       case "touchmove":
         events.has_touch_move = true;
         break;
         
       default:
         break;

       } // endswitch
   } // end loop
 }

 return events;

};

/** 
 * @method EnumerateFaeUtilEvents
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc  Finds the event information of the node for a DOMElement object
 *
 * @param  {Object}     node              - Object The DOM element node that corresponds
 *                                          to this DOMElement object, and from which
 *                                          common information is derived for the DOM
 *                                          element cache.
 *
 * @param  {DOMElement} parent_dom_element - Parent DOMElement object associated with the node's parent node 
 *
 * @return {Object}  Returns an object with event information
 */

OpenAjax.a11y.cache.DOMElement.prototype.EnumerateFaeUtilEvents = function (node, parent_dom_element) {

  function testForEvent(e) {
  
    var has_event = node.getAttribute(e);
    
    if (has_event == 'true') {
      events.supports_events = true;
      return true;  
    }  
    
    return false;

  }

  var events = {};

  events.ancestor_has_blur         = false;
  events.ancestor_has_change       = false;
  events.ancestor_has_click        = false;
  events.ancestor_has_double_click = false;
  events.ancestor_has_focus        = false;
  events.ancestor_has_key_down     = false;
  events.ancestor_has_key_press    = false;
  events.ancestor_has_key_up       = false;
  events.ancestor_has_load         = false;
      
  events.ancestor_has_mouse_down   = false;
  events.ancestor_has_mouse_up     = false;
  events.ancestor_has_mouse_move   = false;
  events.ancestor_has_mouse_out    = false;
  events.ancestor_has_mouse_over   = false;
  events.ancestor_has_mouse_enter  = false;
  events.ancestor_has_mouse_leave  = false;

  events.ancestor_has_drag       = false;
  events.ancestor_has_drag_end   = false;
  events.ancestor_has_drag_enter = false;
  events.ancestor_has_drag_leave = false;
  events.ancestor_has_drag_over  = false;
  events.ancestor_has_drag_start = false;
  events.ancestor_has_drop       = false;

  events.ancestor_has_pointer_up      = false;
  events.ancestor_has_pointer_cancel  = false;
  events.ancestor_has_pointer_move    = false;
  events.ancestor_has_pointer_over    = false;
  events.ancestor_has_pointer_out     = false;
  events.ancestor_has_pointer_enter   = false;
  events.ancestor_has_pointer_leave   = false;

  events.ancestor_has_touch_start   = false;
  events.ancestor_has_touch_end     = false;
  events.ancestor_has_touch_move    = false;
  events.ancestor_has_touch_cancel  = false;

  events.has_blur         = testForEvent('oaa-has-blur');
  events.has_change       = testForEvent('oaa-has-change');
  events.has_click        = testForEvent('oaa-has-click');
  events.has_double_click = testForEvent('oaa-has-double_click');
  events.has_focus        = testForEvent('oaa-has-focus');
    
  events.has_key_down     = testForEvent('oaa-has-key_down');
  events.has_key_press    = testForEvent('oaa-has-key_press');
  events.has_key_up       = testForEvent('oaa-has-key_up');
    
  events.has_load         = testForEvent('oaa-has-load');
    
  events.has_mouse_down   = testForEvent('oaa-has-mouse_down');
  events.has_mouse_up     = testForEvent('oaa-has-mouse_up');
  events.has_mouse_move   = testForEvent('oaa-has-mouse_move');
  events.has_mouse_out    = testForEvent('oaa-has-mouse_out');
  events.has_mouse_over   = testForEvent('oaa-has-mouse_over');
    
  events.has_mouse_enter  = testForEvent('oaa-has-mouse_enter');
  events.has_mouse_leave  = testForEvent('oaa-has-mouse_leave');

  events.has_drag       = testForEvent('oaa-has-drag');
  events.has_drag_end   = testForEvent('oaa-has-drag_end');
  events.has_drag_enter = testForEvent('oaa-has-drag_enter');
  events.has_drag_leave = testForEvent('oaa-has-drag_leave');
  events.has_drag_over  = testForEvent('oaa-has-over');
  events.has_drag_start = testForEvent('oaa-has-start');
  events.has_drop       = testForEvent('oaa-has-drop');

  events.has_pointer_up      = testForEvent('oaa-pointer-up');
  events.has_pointer_cancel  = testForEvent('oaa-pointer-cancel');
  events.has_pointer_move    = testForEvent('oaa-pointer-move');
  events.has_pointer_over    = testForEvent('oaa-pointer-over');
  events.has_pointer_out     = testForEvent('oaa-pointer-out');
  events.has_pointer_enter   = testForEvent('oaa-pointer-enter');
  events.has_pointer_leave   = testForEvent('oaa-pointer-leave');

  events.has_touch_start   = testForEvent('oaa-touch-start');
  events.has_touch_end     = testForEvent('oaa-touch-end');
  events.has_touch_leave   = testForEvent('oaa-touch-leave');
  events.has_touch_move    = testForEvent('oaa-touch-move');
  events.has_touch_cancel  = testForEvent('oaa-touch-cancel');

  if (parent_dom_element && parent_dom_element.events) {
    events.ancestor_has_blur         = parent_dom_element.events.has_blur         || parent_dom_element.events.ancestor_has_blur;
    events.ancestor_has_change       = parent_dom_element.events.has_change       || parent_dom_element.events.ancestor_has_change;
    events.ancestor_has_click        = parent_dom_element.events.has_click        || parent_dom_element.events.ancestor_has_click;
    events.ancestor_has_double_click = parent_dom_element.events.has_double_click || parent_dom_element.events.ancestor_has_double_click;
    events.ancestor_has_focus        = parent_dom_element.events.has_focus        || parent_dom_element.events.ancestor_has_focus;
    events.ancestor_has_key_down     = parent_dom_element.events.has_key_down     || parent_dom_element.events.ancestor_has_key_down;
    events.ancestor_has_key_press    = parent_dom_element.events.has_key_press    || parent_dom_element.events.ancestor_has_key_press;
    events.ancestor_has_key_up       = parent_dom_element.events.has_key_up       || parent_dom_element.events.ancestor_has_key_up;
    events.ancestor_has_load         = parent_dom_element.events.has_load         || parent_dom_element.events.ancestor_has_load;
      
    events.ancestor_has_mouse_down   = parent_dom_element.events.has_mouse_down   || parent_dom_element.events.ancestor_has_mouse_down;
    events.ancestor_has_mouse_up     = parent_dom_element.events.has_mouse_up     || parent_dom_element.events.ancestor_has_mouse_up;
    events.ancestor_has_mouse_move   = parent_dom_element.events.has_mouse_move   || parent_dom_element.events.ancestor_has_mouse_move;
    events.ancestor_has_mouse_out    = parent_dom_element.events.has_mouse_out    || parent_dom_element.events.ancestor_has_mouse_out;
    events.ancestor_has_mouse_over   = parent_dom_element.events.has_mouse_over   || parent_dom_element.events.ancestor_has_mouse_over;
    events.ancestor_has_mouse_enter  = parent_dom_element.events.has_mouse_enter  || parent_dom_element.events.ancestor_has_mouse_enter;
    events.ancestor_has_mouse_leave  = parent_dom_element.events.has_mouse_leave  || parent_dom_element.events.ancestor_has_mouse_leave;

    events.ancestor_has_drag       = parent_dom_element.events.has_drag       || parent_dom_element.events.ancestor_has_drag;
    events.ancestor_has_drag_end   = parent_dom_element.events.has_drag_end   || parent_dom_element.events.ancestor_has_drag_end;
    events.ancestor_has_drag_enter = parent_dom_element.events.has_drag_enter || parent_dom_element.events.ancestor_has_drag_enter;
    events.ancestor_has_drag_leave = parent_dom_element.events.has_drag_leave || parent_dom_element.events.ancestor_has_drag_leave;
    events.ancestor_has_drag_over  = parent_dom_element.events.has_drag_over  || parent_dom_element.events.ancestor_has_drag_over;
    events.ancestor_has_drag_start = parent_dom_element.events.has_drag_start || parent_dom_element.events.ancestor_has_drag_start;
    events.ancestor_has_drop       = parent_dom_element.events.has_drop       || parent_dom_element.events.ancestor_has_drop;

    events.has_pointer_up      = parent_dom_element.events.pointer_up     || parent_dom_element.events.ancestor_pointer_up;
    events.has_pointer_cancel  = parent_dom_element.events.pointer_cancel || parent_dom_element.events.ancestor_pointer_cancel;
    events.has_pointer_move    = parent_dom_element.events.pointer_move   || parent_dom_element.events.ancestor_pointer_move;
    events.has_pointer_over    = parent_dom_element.events.pointer_over   || parent_dom_element.events.ancestor_pointer_over;
    events.has_pointer_out     = parent_dom_element.events.pointer_out    || parent_dom_element.events.ancestor_pointer_out;
    events.has_pointer_enter   = parent_dom_element.events.pointer_enter  || parent_dom_element.events.ancestor_pointer_enter;
    events.has_pointer_leave   = parent_dom_element.events.pointer_leave  || parent_dom_element.events.ancestor_pointer_leave;

    events.ancestor_has_touch_start   = parent_dom_element.events.touch_start  || parent_dom_element.events.ancestor_touch_start;
    events.ancestor_has_touch_end     = parent_dom_element.events.touch_end    || parent_dom_element.events.ancestor_touch_end;
    events.ancestor_has_touch_leave   = parent_dom_element.events.touch_leave  || parent_dom_element.events.ancestor_touch_leave;
    events.ancestor_has_touch_move    = parent_dom_element.events.touch_move   || parent_dom_element.events.ancestor_touch_move;
    events.ancestor_has_touch_cancel  = parent_dom_element.events.touch_cancel || parent_dom_element.events.ancestor_touch_cancel;

  }

  return events;

};


/**
 * @method addChild
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc    Adds a DOMElement or DOMText object to the tree of DOM text/elements  
 *
 * @param  {DOMElement | DOMText} child_object  - DOMElement or DOMText object 
 *
 * @return  Nothing 
 */
 
OpenAjax.a11y.cache.DOMElement.prototype.addChild = function ( child_object ) {

 if (child_object) {
  this.child_dom_elements.push(child_object);
 }

};

/**
 * @method addToCharacterCount
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc  Adds to the current character count of the text content of the
 *          contained in the DOMelement and its immediate children
 *
 * @param {Number} length - Number to add to the character count
 *
 * @return Nothing
 */

OpenAjax.a11y.cache.DOMElement.prototype.addToCharacterCount = function ( length ) {

 this.character_count += length;

};

/**
 * @method addComputedStyle
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc    Adds computed style information to the DOMElement object and
 *          calculate the color contrast ratio
 *
 * @param  {DOMElement} parent_element  - The parent DOMElement object, used
 *                                        for information about inherited style
 *                                        information
 *
 * @return Nothing
 */

OpenAjax.a11y.cache.DOMElement.prototype.addComputedStyle = function (parent_element) {

 this.computed_style = new OpenAjax.a11y.cache.DOMElementComputedStyle(this, parent_element);  
 this.computed_style.calculateColorContrastRatio();
};

/**
 * @method calculateXPath
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc   Calculate the XPath string that uniquely identifies the
 *           DOM node referenced by this DOMElement's node property and
 *           set its xpath property to this calculated value.
 *
 * @param  {DOMElement} parent_element - The parent DOMElement object, used
 *                                       for information for xpath calculation
 *
 * @usage Sets the DOMElement's xpath property
 *
 * @return Nothing 
 */

OpenAjax.a11y.cache.DOMElement.prototype.calculateXPath = function (parent_element) {

 function attributePredicate(attrName, attrValue) {
  return "[@" + attrName + "='" + attrValue + "']";
 }

 this.xpath = "";

 // If a root node ignore calculation
 if (!this.tag_name) {
  return;
 }

 // now build up the XPath using parent, tag_name, id, role and class values
 if (parent_element && parent_element.xpath) {
  this.xpath = parent_element.xpath + "/" + this.tag_name;
 }
 else {
  this.xpath = "/" + this.tag_name;
 }

 if (this.id) {
  this.xpath += attributePredicate("id", this.id);
 }

 if (this.role) {
  this.xpath += attributePredicate("role", this.role);
 }
 else {
  if (this.class_name) {
   this.xpath += attributePredicate("class", this.class_name);
  }
 }

};


/**
 * @method getText
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc Returns text content of a DOMElement, including the ALT text of images
 *       through recursion through the DOMText and DOMElement descendents of
 *       the DOMElement
 *
 * @return {String} Returns the text content of an element and its children
 */
 
OpenAjax.a11y.cache.DOMElement.prototype.getText = function() {

  function getText(dom_element, strings) {
    // If text node get the text and return
    if( dom_element.type == Node.TEXT_NODE ) {
      strings.push( dom_element.text );
    } else {
      // if an element for through all the children elements looking for text
      if( dom_element.type == Node.ELEMENT_NODE ) {
        // check to see if IMG or AREA element and to use ALT content if defined
        if((dom_element.tag_name == 'img') || (dom_element.tag_name == 'area')) {
     
          if (dom_element.alt) {
            strings.push(dom_element.alt);
          } 
          
        } else {
    
          for (var i = 0; i < dom_element.child_dom_elements.length; i++ ) {
            getText(dom_element.child_dom_elements[i], strings);
          } // end loop
          
        } 
      }
    } 
  } // end function getStrings

 // Create return object
 var str = "";
 var strings = [];

 getText(this, strings); 
 
 if (strings.length) str = strings.join(" ").normalizeSpace();
 
 return str;
 
};

 /**
 * @method getTextObject
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 * 
 * @desc Returns an object with information about the accessible text of a DOMElement object
 *         and its descendents
 *
 * @param {Boolean}  visible  - Optional, if true text must be visible to be included. default false
 *
 * @return {Object}  Returns an object with the following properties: 
 *                     'height', 
 *                     'width',
 *                     'image_count',
 *                     'name',
 *                     'name_from_text_nodes',
 *                     'name_from_image_alt',
 */
 
OpenAjax.a11y.cache.DOMElement.prototype.getTextObject = function(visible) {

  if (typeof visible !== 'boolean') visible = false;

  function getText(dom_element, strings, texts, alts) {
    // If text node get the text and return
    if( dom_element.type == Node.TEXT_NODE ) {
      var text = dom_element.text;
      strings.push( text );
      texts.push( text );
    } else {
      // if an element for through all the children elements looking for text
      if( dom_element.type == Node.ELEMENT_NODE ) {
        // check to see if IMG or AREA element and to use ALT content if defined
        if((dom_element.tag_name == 'img') || (dom_element.tag_name == 'area')) {
     
          if (dom_element.alt) {
            strings.push(dom_element.alt);
            alts.push(dom_element.alt);
          }  
     
          if( dom_element.node.offsetHeight > o.height ) {
            o.height = dom_element.node.offsetHeight;
          } //endif
     
          if( dom_element.node.offsetWidth > o.width ) {
             o.width = dom_element.node.offsetWidth;
          } //endif
     
          o.image_count = o.image_count + 1;
     
        } else {
    
          for (var i = 0; i < dom_element.child_dom_elements.length; i++ ) {
            if (!visible || dom_element.computed_style.is_visible_onscreen) {
              getText( dom_element.child_dom_elements[i], strings, texts, alts);
            }  
          } // endfor
     
        } // endif
      } // endif  
    } // endif
  } // end function getStrings

  // Create return object
  var o = {};
  var name_array = [];
  var name_from_text_nodes_array = [];
  var name_from_image_alt_array = [];
  o.height = 0;
  o.width = 0;
  o.image_count = 0;


  if (!visible || this.computed_style.is_visible_onscreen) {
    getText(this, name_array, name_from_text_nodes_array, name_from_image_alt_array); 
  }  
    
 
  o.name         = name_array.join("").normalizeSpace();
  o.name_from_text_nodes = name_from_text_nodes_array.join("").normalizeSpace().toLowerCase();
  o.name_from_image_alt = name_from_image_alt_array.join("").normalizeSpace().toLowerCase();
  return o;
 
}; // end function OpenAjax.cache.util.getAccessibleText


/**
 * @method getElementCount
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 * 
 * @desc Returns a String of the text content of a DOMElement and all its descendent DOMElements
 *
 * @return {Number}  Returns the number of descendent elements in a DOMElement object
 */
 
OpenAjax.a11y.cache.DOMElement.prototype.getElementCount = function() {

  function countElements(dom_element) {
    // If text node get the text and return
    if( dom_element.type == Node.ELEMENT_NODE ) {
      count++;
      for (var i = 0; i < dom_element.child_dom_elements.length; i++ ) {
        countElements(dom_element.child_dom_elements[i]);
      } // end loop
    } 
  } // end function getStrings

 // Create return object
 var count = 0;

 countElements(this); 
 
 return count;
 
};


/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.DOMElement
 *
 * @desc  Create a text String that represents the DOMElement object
 *
 * @return {String}
 */

OpenAjax.a11y.cache.DOMElement.prototype.toString = function() {
 var str = this.tag_name;
 
 if (this.role && this.role.length) return str + "[" + this.role + "]";
 if (this.id && this.id.length) return str + "#" + this.id;
 
 return str;
};

/*
 * Copyright 2011-2013 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
/* ---------------------------------------------------------------- */
/*                            DOMCache                              */
/* ---------------------------------------------------------------- */

/**
 * @constructor DOMCache
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Constructs a DOMCache Object 
 *          
 * @param  {String}  url       - URL of the page being evaluated
 * @param  {String}  title     - The value of title property of the document being evaluated
 * @param  {Object}  document  - The document object reference of the document being evaluated
 *
 * @property {String}  nls       - NLS cache items for properties
 * @property {String}  url       - URL of the page being evaluated
 * @property {String}  base_url  - Base URL of the page being evaluated calculated from the URL
 * @property {String}  title     - The value of title property of the document being evaluated
 * @property {Object}  document  - The document object reference of the document being evaluated
 *
 * DOM cache element objects
 * @property {Object}  element_cache          - dom element cache for all elements
 * @property {Object}  element_with_id_cache  - dom element cache items with a defined id
 *
 * Specialize cache element objects
 * @property {Object}  abbreviations_cache       - Cache of abbreviation elements 
 * @property {Object}  color_contrast_cache      - Cache of abbreviation items
 * @property {Object}  controls_cache            - Cache of form controls and widgets
 * @property {Object}  headings_landmarks_cache  - Cache of headings and abbreviations
 * @property {Object}  images_cache              - Cache of image and area elements
 * @property {Object}  keyboard_focus_cache      - Cache of all interactive elements on a web page
 * @property {Object}  languages_cache           - Cache of language change items
 * @property {Object}  links_cache               - Cache of a and area elements
 * @property {Object}  lists_cache               - Cache of list elements
 * @property {Object}  media_cache               - Cache of media elements
 * @property {Object}  tables_cache              - Cache of table elements
 *
 * @example
 *
 * var dom_cache = new OpenAjax.a11y.cache.DOMCache(url, title, doc, locale); 
 * dom_cache.updateDOMElementCache();
 * dom_cache.updateAllCaches();
 */

OpenAjax.a11y.cache.DOMCache = function (url, title, document) {

 this.nls = OpenAjax.a11y.cache_nls.getCacheNLS();
 
 this.url = url;
 this.base_url = this.url;

 var pos = this.base_url.lastIndexOf('/');
 if (pos >= 0) {
  this.base_url = this.base_url.substring(0,(pos+1));
 }
 else {
  this.base_url = this.base_url + "/";
 }

 this.title = title;
 this.document = document;
 this.lang = document.documentElement.getAttribute('lang');
 
};
 
/**
 * @method initCache
 * @memberOf OpenAjax.a11y.cache.DOMCache
 *
 * @desc Initialize specialized caches
 *    The specialized caches will be updated all at once or or when
 *    needed by a rule depending on how an evaluation is requested
 */

OpenAjax.a11y.cache.DOMCache.prototype.initCache = function () {

 this.element_with_id_cache = new OpenAjax.a11y.cache.DOMElementCache();
 this.element_cache         = new OpenAjax.a11y.cache.DOMElementCache();

 this.abbreviations_cache      = new OpenAjax.a11y.cache.AbbreviationsCache(this);
// this.color_contrast_cache     = new OpenAjax.a11y.cache.ColorContrastCache(this);
 this.text_cache               = new OpenAjax.a11y.cache.TextCache(this);
 this.controls_cache           = new OpenAjax.a11y.cache.ControlsCache(this);
 this.headings_landmarks_cache = new OpenAjax.a11y.cache.HeadingsLandmarksCache(this);
 this.images_cache             = new OpenAjax.a11y.cache.ImagesCache(this);
 this.keyboard_focus_cache     = new OpenAjax.a11y.cache.KeyboardFocusCache(this);
 this.languages_cache          = new OpenAjax.a11y.cache.LanguagesCache(this);
 this.links_cache              = new OpenAjax.a11y.cache.LinksCache(this);
 this.lists_cache              = new OpenAjax.a11y.cache.ListsCache(this);
 this.media_cache              = new OpenAjax.a11y.cache.MediaCache(this);
 this.tables_cache             = new OpenAjax.a11y.cache.TablesCache(this);
 
 this.frame_count = 0;
 this.iframe_count = 0;

 // Page information
 this.element_information         = new OpenAjax.a11y.cache.ElementInformation();

};

/**
 * @method isUpToDate
 * @memberOf OpenAjax.a11y.cache.DOMCache
 *
 * @desc Checks to see if the specified cache is up to date
 *
 * @param cache_name String Cache to update
 *
 * @return Object with two properties:
 *     o.up_to_date Boolean true if cache is up to date, otherwise false
 *     o.exists   Boolean true if cache exists, otherwise false
 */

OpenAjax.a11y.cache.DOMCache.prototype.isUpToDate = function (cache_name) {

 if (this[cache_name])
  return { up_to_date: this[cache_name].up_to_date, exists : true };
 else
  return { up_to_date: false, exists : false };

};

/**
 * @method updateCache
 *
 * @memberOf OpenAjax.a11y.cache.DOMCache
 *
 * @desc Updates the specified cache
 *
 * @param cache_name String name of the cache to update
 *
 * @return {Boolean} Returns true if cache is updated, false if cache does not exist
 */

OpenAjax.a11y.cache.DOMCache.prototype.updateCache = function (cache_name) {

 if (this[cache_name]) {
  if (!this[cache_name].up_to_date) {
   this[cache_name].updateCache();
  }
  return true;
 }

 return false;

};

/**
 * @method traverseDOMElementsForAllCaches
 * @memberOf OpenAjax.a11y.cache.DOMCache
 *
 * @desc Updates all the specialized caches at one time, in general this
 *    is faster than updating the caches individually based on the
 *    needs of rules, but may create caches that will not be used if
 *    some rules are disabled
 *
 * @param  dom_element            Object  Current DOMElement object being processed
 * @param  landmark_info          Object  LandmarkInfo object containing current landmark and heading information for tree representations
 * @param  heading_global_info    Object  HeadingInfo object containing current heading information used for heading nesting rules
 * @param  heading_landmark_info  Object  HeadingInfo object containing current heading information used for heading nesting rules
 * @param  table_info             Object  TableInfo object containing current table information for tree representations
 * @param  control_info           Object  ControlInfo object containing current control information for tree representations
 * @param  list_info              Object  Current LanguageElement object that contains the DOMElement
 *
 * @return none
 */

OpenAjax.a11y.cache.DOMCache.prototype.traverseDOMElementsForAllCaches = function (dom_element,
                                          landmark_info,
                                          heading_global_info,
                                          table_info,
                                          control_info,
                                          list_info,
                                          media_info) {

 if (!dom_element) return;
 // if an element for through all the children elements looking for text

 if (dom_element.type == Node.ELEMENT_NODE) {

  this.abbreviations_cache.updateCacheItems(dom_element);
  this.images_cache.updateCacheItems(dom_element);
  this.languages_cache.updateCacheItems(dom_element);
  this.links_cache.updateCacheItems(dom_element);
  
  var mi = this.media_cache.updateCacheItems(dom_element, media_info);
  var ci = this.controls_cache.updateCacheItems(dom_element, control_info);
  var hi = this.headings_landmarks_cache.updateCacheItems(dom_element, landmark_info, heading_global_info);
  var li = this.lists_cache.updateCacheItems(dom_element, list_info);
  var ti = this.tables_cache.updateCacheItems(dom_element, table_info);

  var children_length = dom_element.child_dom_elements.length;
  for (var i = 0; i < children_length; i++ ) {
   this.traverseDOMElementsForAllCaches(dom_element.child_dom_elements[i], hi, heading_global_info, ti, ci, li, mi);
  } // end loop
  
  this.element_information.countElement(dom_element);
  
 } else {
   this.text_cache.updateCacheItems(dom_element);
   this.headings_landmarks_cache.updateCacheItems(dom_element, landmark_info, heading_global_info);
 }
};


/**
 * @method updateAllCaches
 * @memberOf OpenAjax.a11y.cache.DOMCache
 *
 * @desc Traverses the DOMElements and 
 *       calls the update function to see which specialized caches want information on this element
 *
 * @return none
 */

OpenAjax.a11y.cache.DOMCache.prototype.updateAllCaches = function () {
 var i;
 var children = this.element_cache.child_dom_elements;
 var children_len = children.length;

  var hi = new OpenAjax.a11y.cache.LandmarkInfo(null);
  var hi_g = new OpenAjax.a11y.cache.HeadingInfo(null);
  var ti = new OpenAjax.a11y.cache.TableInfo(null);
  var ci = new OpenAjax.a11y.cache.ControlInfo(null);
  var li = new OpenAjax.a11y.cache.ListInfo(null);
  var mi = new OpenAjax.a11y.cache.MediaInfo();
 

  for (i=0; i < children_len; i++) {
    this.traverseDOMElementsForAllCaches(children[i], hi, hi_g, null, ti, ci, li, mi);
  }

  this.controls_cache.calculateControlLabels();
  this.controls_cache.applyAriaOwns();
 
  this.keyboard_focus_cache.createKeyboardFocusCache();

};

/**
 * @method updateDOMElementCache
 * @memberOf OpenAjax.a11y.cache.DOMCache
 *
 * @desc Updates a DOMElement object caches by traversing the DOM of the browser object
 *
 * @return DOMCache Object
 */

OpenAjax.a11y.cache.DOMCache.prototype.updateDOMElementCache = function () {

 var de;

 this.initCache();
 
 // add title information to DOMElement Cache
 
 this.addTitleDOMElement();

 // if the page contains a body element start there, since there are fewer elements to traverse
 if (this.document && this.document.body) {
  // OpenAjax.a11y.logger.debug("Creating DOM elements from body element");
  this.updateDOMElements(this.document.body, null, null);
 }
 // If there are frames start at the top element
 else {
  // OpenAjax.a11y.logger.debug("Creating DOM elements with frames");
  this.updateDOMElements(this.document, null, null);
 }

 // Calculate aria-descriptions
 this.calculateDescriptions();

 return this;

};

/**
 * @method addTitleDOMElement
 * @memberOf OpenAjax.a11y.cache.DOMCache
 *
 * @desc Adds a DOMElement to represent a TITLE
 *
 * @return Nothing
 */

OpenAjax.a11y.cache.DOMCache.prototype.addTitleDOMElement = function () {

  var n;
  var node;
  var de;
  var titles = this.document.getElementsByTagName('title');

  if (titles && titles.length && titles[0]) {
  
    node = titles[0];

    de = new OpenAjax.a11y.cache.DOMElement(node, null);
    
    this.document_has_title = true;

    de.addComputedStyle(null);
    de.calculateXPath(null);
  
    this.element_cache.addDOMElement(de);
    this.element_cache.addChild(de);
 
    // get any text nodes associated with the title element
    for (n = node.firstChild; n !== null; n = n.nextSibling) {
      this.updateDOMElements( n, de, null);
    } // end loop
    
  }
  else {
  
    node = this.document.createElement('title');
    
    de = new OpenAjax.a11y.cache.DOMElement(node, null);
    
    this.document_has_title = false;

    de.addComputedStyle(null);
    de.xpath = "";
  
    this.element_cache.addDOMElement(de);
    this.element_cache.addChild(de);

  }

};

/**
 * @method updateDOMElements
 * @memberOf OpenAjax.a11y.cache.DOMCache
 *
 * @desc Traverse document DOM and create a tree of DOMElement objects.
 *    The DOMElement objects will be used to generate more specific
 *    lists of elements without need to touch the document DOM.
 *    Additional information is collected on tables to be used in
 *    creating the table cache
 *
 * @param  {Object} node               - node is the current node object tbing analyzed
 * @param  {Object} parent_dom_element - DOMElement object that is the parent of the current node
 * @param  {Object} previous_sibling   - The DOMElement or DOMText object that is the previous sibling
 *
 * return nothing
 */

OpenAjax.a11y.cache.DOMCache.prototype.updateDOMElements = function (node, parent_dom_element, previous_sibling) {

  var n;
  var de;

  switch (node.nodeType ) {

  case Node.DOCUMENT_NODE:
  case Node.DOCUMENT_TYPE_NODE:
    // OpenAjax.a11y.logger.debug("Document node type");
    break;

  case Node.ELEMENT_NODE:
    // OpenAjax.a11y.logger.debug(node.tagName);
    
    var dom_element = new OpenAjax.a11y.cache.DOMElement(node, parent_dom_element);

    dom_element.addComputedStyle(parent_dom_element);
    
    dom_element.calculateXPath(parent_dom_element);
    this.element_cache.addDOMElement(dom_element);

    if (parent_dom_element) {
      parent_dom_element.has_element_children = true;
      parent_dom_element.addChild(dom_element);
    }
    else {
      this.element_cache.addChild(dom_element);
    }

    if (dom_element.id && dom_element.id.length) {
      // use append so that document_order of the dom_element does not get updated
      
      de = this.element_with_id_cache.getDOMElementById(dom_element.id);
      
      if (de) {
        dom_element.id_unique = OpenAjax.a11y.ID.NOT_UNIQUE;
        de.id_unique = OpenAjax.a11y.ID.NOT_UNIQUE;
      }
      
      this.element_with_id_cache.dom_elements.push(dom_element);
            
    }

    switch (dom_element.tag_name) {

    case 'frame':
    case 'iframe':

      if (dom_element.tag_name === 'frame') this.frame_count += 1;
      else this.iframe_count += 1;

      var frame_doc = node.contentWindow.document;

//      OpenAjax.a11y.logger.debug("frame: " + node.src + " " + frame_doc);

      if (frame_doc && frame_doc.firstChild) {
        for (n = frame_doc.firstChild; n !== null; n = n.nextSibling) {
          this.updateDOMElements( n, dom_element, null);
        } // end loop
      }
      break;

    default:
      break;

    } // end switch
    
    var ps = null;

    for (n = node.firstChild; n !== null; n = n.nextSibling ) {
      ps = this.updateDOMElements(n, dom_element, ps);
    } // end loop
    
    return dom_element;
    break;

  case Node.TEXT_NODE:
   // OpenAjax.a11y.logger.debug("DOM node text: " + node.data);

   var dom_text = new OpenAjax.a11y.cache.DOMText(node, parent_dom_element);

   if (dom_text.text_length) {
   
     if (!previous_sibling || previous_sibling.type === Node.ELEMENT_NODE) {
   
       this.element_cache.addDOMText(dom_text);
       if (parent_dom_element) parent_dom_element.addChild(dom_text);
       return dom_text;
     
     } else {
   
       if (previous_sibling) previous_sibling.addText(dom_text.text);
       return previous_sibling;
     }  
   }
   else {
     return previous_sibling;
   }
   
   break;

  default:
    break;
  } // end switch

  return null;

};


/**
 * @method calculateDescriptions
 *
 * @memberOf OpenAjax.a11y.cache.DOMCache
 *
 * @desc Calculates a description if a element has an aria-describedby attribute defined
 */

OpenAjax.a11y.cache.DOMCache.prototype.calculateDescriptions = function () {

  var de;
  var dom_elements     = this.element_cache.dom_elements;
  var dom_elements_len = dom_elements.length;

  for (var i = 0; i < dom_elements_len; i++ ) {
    de = dom_elements[i];
    
    if (typeof de.aria_describedby === 'string' && de.aria_describedby.length && !de.calculated_aria_description) {
      de.calculated_aria_description = this.getTextFromIDs(de.aria_describedby);  
    }
  }
};

/**
 * @method getNameForLink
 *
 * @memberOf OpenAjax.a11y.cache.DOMCache
 *
 * @desc Calculates a computed accessible name for a link, using ARIA properties if defined
 *
 * @param {LinkElement} link - Link element object
 */

OpenAjax.a11y.cache.DOMCache.prototype.getNameForLink = function (link) {

  var SOURCE = OpenAjax.a11y.SOURCE;

  var computed_label = "";
  var computed_label_source = SOURCE.NONE;
  var de = link.dom_element;
  
  if (de.aria_labelledby) {
    computed_label = this.element_with_id_cache.getTextFromIds(de.aria_labelledby);
    computed_label_source = SOURCE.ARIA_LABELLEDBY;
  }
  else if (de.aria_label) {
    computed_label = de.aria_label;
    computed_label_source = SOURCE.ARIA_LABEL;
  }
  else {
    computed_label = de.getText();
    computed_label_source = SOURCE.TEXT_CONTENT;
  } 

  link.accessible_name                = computed_label;
  link.accessible_name_for_comparison = computed_label.toLowerCase().normalizeSpace();
  link.accessible_name_length         = computed_label.length;
  link.accessible_name_source         = computed_label_source;

  this.getDescriptionFromARIADescribedby(link);
};


/**
 * @method getNameForTable
 *
 * @memberOf OpenAjax.a11y.cache.DOMCache
 *
 * @desc Calculates a computed accessible name for a table element, using ARIA properties if defined
 *
 * @param {TableElement} table - Table element object
 */

OpenAjax.a11y.cache.DOMCache.prototype.getNameForTable = function (table, caption) {

  var SOURCE = OpenAjax.a11y.SOURCE;

  var computed_label = "";
  var computed_label_source = SOURCE.NONE;
  var de = table.dom_element;
  
  if (typeof caption !== 'string') {
  
    if (de.aria_labelledby) {
      computed_label = this.element_with_id_cache.getTextFromIds(de.aria_labelledby);
      computed_label_source = SOURCE.ARIA_LABELLEDBY;
    }
    else if (de.aria_label) {
      computed_label = de.aria_label;
      computed_label_source = SOURCE.ARIA_LABEL;
    }
    else {
      var summary = de.node.getAttribute('summary');
      if ((typeof summary === 'string') && summary.length) {
        computed_label = summary;
        computed_label_source = SOURCE.TABLE_SUMMARY;
      }
      else {
        var title = de.node.getAttribute('title');
        if ((typeof title === 'string') && title.length) {
          computed_label = title;
          computed_label_source = SOURCE.TITLE_ATTRIBUTE;
        }
      }
    }
  }
  else {  
    computed_label = caption;
    computed_label_source = SOURCE.TABLE_CAPTION;
  } 

  table.accessible_name                = computed_label;
  table.accessible_name_for_comparison = computed_label.toLowerCase().normalizeSpace();
  table.accessible_name_length         = computed_label.length;
  table.accessible_name_source         = computed_label_source;

  this.getDescriptionFromARIADescribedby(table);
};

/**
 * @method getNameFromARIALabel
 *
 * @memberOf OpenAjax.a11y.cache.DOMCache
 *
 * @desc Calculates a computed label and accessible name based on ARIA properties
 *
 * @param {Object} control - Control cache element object
 */

OpenAjax.a11y.cache.DOMCache.prototype.getNameFromARIALabel = function (control) {

  var SOURCE = OpenAjax.a11y.SOURCE;

  var computed_label = "";
  var computed_label_source = SOURCE.NONE;
  var de = control.dom_element;
  var wi = de.role_info;
  
  if (de.aria_labelledby) {
    computed_label = this.element_with_id_cache.getTextFromIds(de.aria_labelledby);
    computed_label_source = SOURCE.ARIA_LABELLEDBY;
  }
  else if (de.aria_label) {
    computed_label = de.aria_label;
    computed_label_source = SOURCE.ARIA_LABEL;
  }
  else if (wi && wi.nameFromContent) {
    computed_label = de.getText();
    computed_label_source = SOURCE.TEXT_CONTENT;
  } else if (de.title) {
    computed_label = de.title;
    computed_label_source = SOURCE.TITLE_ATTRIBUTE;
  }

  control.computed_label = computed_label;
  control.computed_label_length = computed_label.length;
  control.computed_label_source = computed_label_source;
  control.computed_label_for_comparison = computed_label.normalizeSpace().toLowerCase();
  control.accessible_name = computed_label;

  this.getDescriptionFromARIADescribedby(control);
};

/**
 * @method getDescriptionFromARIADescribedby
 *
 * @memberOf OpenAjax.a11y.cache.DOMCache
 *
 * @desc Calculates a description based on ARIA properties
 *
 * @param {Object} element - Cache element object
 */

OpenAjax.a11y.cache.DOMCache.prototype.getDescriptionFromARIADescribedby = function (element) {

  var de = element.dom_element;  
  if (typeof de !== 'object') de = element;
  
  if (de && de.aria_describedby) {
    element.accessible_description = this.element_with_id_cache.getTextFromIds(de.aria_describedby);
    element.accessible_description_for_comparison = element.accessible_description.toLowerCase().normalizeSpace();
  }
  else {
    element.accessible_description = "";  
    element.accessible_description_for_comparison = "";
  }
  
};


/**
 * @method getTextFromIDs
 * @memberOf OpenAjax.a11y.cache.DOMCache
 *
 * @desc Returns the text content of the elements identified in the list of ids
 *
 * @param {String}  ids  An string with space separated ids
 *
 * @return String
 */

OpenAjax.a11y.cache.DOMCache.prototype.getTextFromIDs = function (ids) {

  if (!ids || ids.length === 0) return ""; 
  
  return this.element_with_id_cache.getTextFromIds(ids);

};

/**
 * @method sortArrayOfObjects
 * @memberOf OpenAjax.a11y.cache.DOMCache
 *
 * @desc Sort an array of objects by one of its properties and marks any properties that are duplicates
 *
 * @param {Array}   objects   - Array of objects to sort
 * @param {String}  property  - Text string of property to sort
 * @param {Boolean} ascending - True sort by ascending values otherwise sort by descending values
 *
 * @return Array of sorted objects
 */

OpenAjax.a11y.cache.DOMCache.prototype.sortArrayOfObjects = function(objects, property, ascending ) {

  var swapped = false;
  var temp = null;
  var i;
  var return_objects = [];

  if( !objects && objects.length && !objects[0][property] ) {
    return return_objects;
  } // endif

  var objects_len = objects.length;
 
  for (i = 0; i < objects_len; i++) {
    return_objects[i] = objects[i];
    return_objects[i].duplicate = false;
  }  

  if( ascending ) {
    do{
      swapped = false;
      for (i = 1; i < objects_len; i++ ) {
        if (return_objects[i-1][property] > return_objects[i][property]) {
          // swap the values
          temp = return_objects[i-1];
          return_objects[i-1] = return_objects[i];
          return_objects[i] = temp;
          swapped = true;
        } 
        else {
          if (return_objects[i-1][property] === return_objects[i][property]) {
            return_objects[i-1].duplicate = true;
            return_objects[i].duplicate = true;
          }          
        }
      } // end loop
    } while (swapped);
  }
  else {
    do {
      swapped = false;
      for (i = 1; i < objects_len; i++) {
        if (return_objects[i-1][property] < return_objects[i][property]) {
          // swap the values
          temp = return_objects[i-1];
          return_objects[i-1] = return_objects[i];
          return_objects[i] = temp;
          swapped = true;
        } 
        else {
          if (return_objects[i-1][property] === return_objects[i][property]) {
            return_objects[i-1].duplicate = true;
            return_objects[i].duplicate = true;
          }          
        }
      } // end loop
    } while (swapped);
  } 

  return return_objects;

};

/**
 * Copyright 2011-2013 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* ---------------------------------------------------------------- */
/*            OpenAjax Heading and Landmark Cache                   */ 
/* ---------------------------------------------------------------- */

/* ---------------------------------------------------------------- */
/*                    LandmarkInfo Object                           */ 
/* ---------------------------------------------------------------- */

/**
 * @constructor LandmarkInfo
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc LandmarkInfo is the constructor for information related to landmarks
 *       in building the headings/landmark cache
 *
 * @param {LandmarkInfo}  landmark_info - Current landmark (if any)
 *
 * @property {Landmark Object}  landmark_element  - parent landmark element 
 * @property {Landmark Object}  main_element      - parent main landmark element 
 * @property {Cache    Object}  page_element      - page object element 
 * @property {Cache    Object}  body_element      - body page object element 
 * @property {Cache    Object}  iframe_element    - iframe object element 
 */

OpenAjax.a11y.cache.LandmarkInfo = function (landmark_info) {

  if (landmark_info) {
    this.landmark_element = landmark_info.landmark_element;
    this.main_element     = landmark_info.main_element;
    this.page_element     = landmark_info.page_element;
    this.body_element     = landmark_info.body_element;
    this.iframe_element   = landmark_info.iframe_element;
    
  }
  else {
    this.landmark_element = null;
    this.main_element     = null;
    this.page_element     = null;
    this.body_element     = null;
    this.iframe_element   = null;
    
  }
 
};

/**
 * @constructor HeadingInfo
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc HeadingInfo is the constructor for information related to heading nesting
 *       in building the headings/landmark cache
 *
 * @param {HeadingInfo}  heading_info - Current heading information (if any)
 *
 * @property {Heading Object}  nesting_h1  - Visible heading object at H1 level 
 * @property {Heading Object}  nesting_h2  - Visible heading object at H2 level 
 * @property {Heading Object}  nesting_h3  - Visible heading object at H3 level 
 * @property {Heading Object}  nesting_h4  - Visible heading object at H4 level 
 * @property {Heading Object}  nesting_h5  - Visible heading object at H5 level 
 */

OpenAjax.a11y.cache.HeadingInfo = function (heading_info) {

  if (heading_info) {
    this.is_past_first_h1 = landmark_info.is_past_first_h1;
    this.nesting_h1       = landmark_info.nesting_h1;
    this.nesting_h2       = landmark_info.nesting_h2;
    this.nesting_h3       = landmark_info.nesting_h3;
    this.nesting_h4       = landmark_info.nesting_h4;
    this.nesting_h5       = landmark_info.nesting_h5;
  }
  else {
    this.is_past_first_h1 = false;
    this.nesting_h1       =  null;
    this.nesting_h2       =  null;
    this.nesting_h3       =  null;
    this.nesting_h4       =  null;
    this.nesting_h5       =  null;
  }
 
};

/* ---------------------------------------------------------------- */
/*                     HeadingsLandmarksCache                       */ 
/* ---------------------------------------------------------------- */

/**
 * @constructs HeadingsLandmarksCache
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc HeadingsLandmarksCache is the constructor for lists of heading and landmrk element objects and
 *       the root of a tree representation of the landmark and heading element relationships
 *
 * @param {DOMCache}  dom_cache   - Reference to the DOMCache object 
 * 
 * @property {DOMCache} dom_cache  - Reference to the DOMCache object 
 *         
 * @property {Boolean}  up_to_date - Boolean true if the cache has been creating using the current DOMElements, else false
 *                                   NOTE: This is a common property of all caches and is used when selectively build caches 
 *                                          based on whether a rule needs the cache
 *
 * @property {Array}  child_cache_elements  - Root array of the tree representation of the landmarks and headings in the document 
 *
 * @property {Array}   landmark_elements        - List of all the landmark elements in the document 
 * @property {String}  landmarks_sort_property  - Name of the landmark element property the landmark elements array is currently sorted
 * @property {Number}  landmark_length          - The length of the landmark elements list, used in calculating cache id values 
 *
 * @property {Array}   heading_elements        - List of all the heading elements in the document 
 * @property {String}  headings_sort_property  - Name of the heading element property the heading elements array is currently sorted
 * @property {Number}  heading_length          - The length of the heading elements list, used in calculating cache id values 
 *
 * @property {Array}   elements_with_content   - List of content elements and text nodes outside of landmarks 
 *
 * @property {Array}   main_elements  - List of all the main landmark elements in the document 
 * @property {Number}  main_length    - The length of the main landmark elements list, used in calculating cache id values 
 *
 * @property {Array}   h1_elements    - List of all the h1 heading elements in the document 
 * @property {Number}  h1_length      - The length of the main landmark elements list, used in calculating cache id values 
 *
 * @property {Boolean}  has_main_landmarks  - True if document contians at lewast one main landmark, otherwise false
 * @property {Boolean}  has_title           - Title element is defined in the document 
 *
 * @property {TitleElement} title_element  - The title element is used as a placeholder for title rule results 
 * @property {PageElementHeadingsLandmarks}  page_element   - The body element is used as a placeholder rule results for items missing in a document like H1 elements and Main landmarks
 *
 * @property {ResultRuleSummary}  rule_summary_result  - Rule results associated with this cache
 */

OpenAjax.a11y.cache.HeadingsLandmarksCache = function (dom_cache) {

  this.dom_cache = dom_cache;
  
  this.up_to_date    = false;
 
  this.child_cache_elements   = [];
  
  this.landmark_elements = [];
  this.landmarks_sort_property = 'document_order';
  this.landmark_length  = 0;
  
  this.heading_elements = [];  
  this.headings_sort_property  = 'document_order';
  this.heading_length  = 0;
  
  this.elements_with_content = [];
  
  this.main_elements = [];
  this.main_length   = 0;

  this.h1_elements   = [];
  this.h1_length     = 0;
  
  this.has_h1_elements    = false;
  this.has_main_landmarks = false;
  this.has_title          = false;

  this.title_element = null;  
  this.page_element  = null;  

  this.iframe_elements = [];

};

/**
 * @method addChildElement
 *
 * @memberOf OpenAjax.a11y.cache.HeadingsLandmarksCache
 *
 * @desc Adds a landmark or header element object to the root level of a tree of landmark/heading elements  
 *
 * @param {LandmarkElement | HeadingElement} child_element - Landmark or heading element object to add
 */

OpenAjax.a11y.cache.HeadingsLandmarksCache.prototype.addChildElement = function (child_element) {

  // item must exist and have the position property
  if (child_element) {
    this.child_cache_elements.push(child_element);
  } 

};

/**
 * @method addLandmarkElement
 *
 * @memberOf OpenAjax.a11y.cache.HeadingsLandmarksCache
 *
 * @desc   Adds a landmark element object to the heading elements list
 *
 * @param  {LandmarkElement} heading_element  - Landmark element object to a landmark elements list
 *
 * @return {Number} Returns the length of the landmark elements list
 */

OpenAjax.a11y.cache.HeadingsLandmarksCache.prototype.addLandmarkElement = function (landmark_element) {

  if (landmark_element) {
    this.landmark_length = this.landmark_length + 1;
    landmark_element.document_order = this.landmark_length;
    landmark_element.cache_id = "landmark_" + this.landmark_length;
    this.landmark_elements.push(landmark_element);
  } 
  
  return this.landmark_length;
};

/**
 * @method addHeadingElement
 *
 * @memberOf OpenAjax.a11y.cache.HeadingsLandmarksCache
 *
 * @desc   Adds a heading element object to the heading elements list
 *
 * @param  {HeadingElement} heading_element  - HeadingElement object to a heading_elements array
 *
 * @return {Number} Returns the length of the heading elements list
 */

OpenAjax.a11y.cache.HeadingsLandmarksCache.prototype.addHeadingElement = function (heading_element) {

  if (heading_element) {
    this.heading_length = this.heading_length + 1;
    heading_element.document_order = this.heading_length;
    heading_element.cache_id = "heading_" + this.heading_length;
    this.heading_elements.push(heading_element);
  } 
  return this.heading_length;
};

/**
 * @method addChildMainElement
 *
 * @memberOf OpenAjax.a11y.cache.HeadingsLandmarksCache
 *
 * @desc Adds a main landmark or h1 heading element object to the root level of a tree of title and main elements  
 *
 * @param {MainElement | H1Element}  child_element - Main landmark or h1 heading element object to add
 */

OpenAjax.a11y.cache.HeadingsLandmarksCache.prototype.addChildMainElement = function (child_element) {

  // item must exist and have the position property
  if (child_element) {
    this.child_cache_elements.push(child_element);
  } 

};

/**
 * @method addH1Element
 *
 * @memberOf OpenAjax.a11y.cache.HeadingsLandmarksCache
 *
 * @desc   Adds a h1 element object to the h1 heading elements list
 *
 * @param  {H1Element}  h1_element  -  h1 heading element to add 
 */

OpenAjax.a11y.cache.HeadingsLandmarksCache.prototype.addH1Element = function (h1_element) {

  if (h1_element && h1_element.main_type === OpenAjax.a11y.MAIN.H1_ELEMENT) {
    this.h1_length = this.h1_length + 1;
    h1_element.document_order = this.h1_length;
    h1_element.cache_id = "h1_" + this.h1_length;
    this.h1_elements.push(h1_element);
  } 

};

/**
 * @method addMainElement
 *
 * @memberOf OpenAjax.a11y.cache.HeadingsLandmarksCache
 *
 * @desc    Adds a main, h1 or title element object to the main_elements array and cacluates a cache id value
 *
 * @param  {MainElement | H1Element | TitleElement | PageElementHeadingsLandmarks}  main_element  Main, h1 heading or title element object to add 
 *
 * @return  {Number}  length is the number of elements in the main_elements list
 */

OpenAjax.a11y.cache.HeadingsLandmarksCache.prototype.addMainElement = function (main_element) {

  if (main_element) {
    this.main_length = this.main_length + 1;
    main_element.document_order = this.main_length;
    main_element.cache_id = "main_" + this.main_length;
    this.main_elements.push(main_element);
  } 
  
  return this.length;
};


/**
 * @method getItemByCacheId
 *
 * @memberOf OpenAjax.a11y.cache.HeadingsLandmarksCache
 *
 * @desc  Finds the landmark or heading element object with the cache id value 
 *
 * @param  {String}  cache_id  - cache id of a landmark element object
 *
 * @return  {LandmarkElement | HeadingElement | null} Returns a landmark element object if cache id found, otherwise null
 */

OpenAjax.a11y.cache.HeadingsLandmarksCache.prototype.getItemByCacheId = function (cache_id) {

  var i;
  var elements_with_content     = this.elements_with_content;
  var elements_with_content_len = elements_with_content.length;
  var item = null;

  item = this.getLandmarkElementByCacheId(cache_id);
  if (item) return item;
  
  item = this.getHeadingElementByCacheId(cache_id);
  if (item) return item;

  for (i = 0; i < elements_with_content_len; i++) {
    item = this.elements_with_content[i];
    if (item.cache_id == cache_id) return item;
  }

  return item;

};

/**
 * @method getLandmarkElementByCacheId
 *
 * @memberOf OpenAjax.a11y.cache.HeadingsLandmarksCache
 *
 * @desc  Finds the landmark element object with the cache id value 
 *
 * @param  {String}  cache_id  - cache id of a landmark element object
 *
 * @return  {LandmarkElement | null} Returns a landmark element object if cache id found, otherwise null
 */

OpenAjax.a11y.cache.HeadingsLandmarksCache.prototype.getLandmarkElementByCacheId = function (cache_id) {
 
  var i; 
  var landmark_elements_len = this.landmark_elements.length;

  if (cache_id) {
    for (i=0; i<landmark_elements_len; i++) {
      if (this.landmark_elements[i].cache_id == cache_id) {
        return this.landmark_elements[i];
      } 
    } // end loop
  }
 return null;
};

/**
 * @method getHeadingElementByCacheId
 *
 * @memberOf OpenAjax.a11y.cache.HeadingsLandmarksCache
 *
 * @desc  Finds the heading element object with the cache id value 
 *
 * @param  {String}  cache_id  - cache id of a heading element object
 *
 * @return  {HeadingElement | null}  Returns a heading object if cache id found, otherwise null
 */

OpenAjax.a11y.cache.HeadingsLandmarksCache.prototype.getHeadingElementByCacheId = function (cache_id) {
 
  var i;
  var heading_elements_len = this.heading_elements.length;

  if (cache_id) {
    for (i=0; i<heading_elements_len; i++) {
      if (this.heading_elements[i].cache_id == cache_id) {
        return this.heading_elements[i];
      } 
    } // end loop
  }
 
  return null;
};

/**
 * @method initCache
 *
 * @memberOf OpenAjax.a11y.cache.HeadingsLandmarksCache
 *
 * @desc Empties the landmark and headings cache 
 */

OpenAjax.a11y.cache.HeadingsLandmarksCache.prototype.initCache = function () {

  this.child_cache_elements  = [];
  
  this.up_to_date = false;
 
  this.landmark_elements = [];
  this.landmark_length = 0;
  this.landmarks_sort_property = 'document_order';
  
  this.heading_elements = [];
  this.heading_length = 0;
  this.headings_sort_property = 'document_order';
 
  this.main_elements = [];
  this.main_length   = 0;
  
  this.h1_elements   = [];
  this.h1_length     = 0;
  
  this.page_element = null;
  
  this.has_h1_elements    = false;
  this.has_main_landmarks = false;
  this.has_title          = false;
  
  this.iframe_elements = [];

};

/**
 * @method updateCacheItems
 *
 * @memberOf OpenAjax.a11y.cache.HeadingsLandmarksCache
 *
 * @desc Updates the landmarks and headings cache by checking to see if a DOMElement
 *          should be added
 *  
 * @param  {DOMElement}    dom_element    - DOMElement object to check for inclusion in links cache
 * @param  {LandmarkInfo}  landmark_info  - Information about the current landmarks that are parents to this item
 * @param  {HeadingInfo}   heading_info   - Information about the current headers used for nesting rules
 * 
 * @return {LandmarkInfo}  Returns updated landmark info object
 */
 
OpenAjax.a11y.cache.HeadingsLandmarksCache.prototype.updateCacheItems = function (dom_element, landmark_info, heading_info) {

  var me;
  var le;
  var he;
  var li = new OpenAjax.a11y.cache.LandmarkInfo(landmark_info);
  var tag_name = dom_element.tag_name;
  
  dom_element.parent_landmark = landmark_info.landmark_element;
  
  if (dom_element.type == Node.ELEMENT_NODE) {

    if (dom_element.is_landmark) {
    
      if (dom_element.role == 'main') {
   
        this.has_main_landmarks = true;
 
        me = new OpenAjax.a11y.cache.MainElement(dom_element, dom_element, landmark_info.landmark_element, landmark_info.body_element);    

        this.dom_cache.getNameFromARIALabel(me);

        this.addLandmarkElement(me);
        this.addMainElement(me);  

        if (landmark_info.landmark_element) {
          landmark_info.landmark_element.addChildElement(me);
        } 
        else {
          this.addChildElement(me);  
        }

        li.landmark_element = me;
        li.main_element     = me;
    
        return li;
      }
      else {
   
        le = new OpenAjax.a11y.cache.LandmarkElement(dom_element, landmark_info.landmark_element, landmark_info.body_element);    

        this.dom_cache.getNameFromARIALabel(le);

        this.addLandmarkElement(le);

        if (landmark_info.landmark_element) {
          landmark_info.landmark_element.addChildElement(le);
        } 
        else {
          this.addChildElement(le);  
        }
  
        li.landmark_element = le;

        return li;
      }  
    }
    
    if (tag_name == 'h1') {
  
      this.has_h1_elements = true;

      he = new OpenAjax.a11y.cache.H1Element(dom_element, landmark_info.landmark_element, landmark_info.main_element);    

      this.addH1Element(he);
      this.addHeadingElement(he);

      if (landmark_info.main_element) { 
        landmark_info.main_element.addH1Element(he);
      }  

      if (landmark_info.landmark_element) {
        landmark_info.landmark_element.addChildElement(he);
      } 
      else {
        this.addChildElement(he);  
      }

      he.isH1UsedAsLabelForMainRole();

      if ((dom_element.computed_style.is_visible_to_at === OpenAjax.a11y.VISIBILITY.VISIBLE) &&
            he.name_for_comparison.length) {

        heading_info.is_past_first_h1 = true;
        heading_info.nesting_h1       = he;
        heading_info.nesting_h2       = null;
        heading_info.nesting_h3       = null;
        heading_info.nesting_h4       = null;
        heading_info.nesting_h5       = null;

        if (li.landmark_element) {
       
          var hi = li.landmark_element.heading_info;

          hi.nesting_h1 = he;
          hi.nesting_h2 = null;
          hi.nesting_h3 = null;
          hi.nesting_h4 = null;
          hi.nesting_h5 = null;

          OpenAjax.a11y.logger.debug("========================");
          OpenAjax.a11y.logger.debug("Heading Nesting: " + he + " (" + li.landmark_element + ")");
          OpenAjax.a11y.logger.debug("  h1: " + hi.nesting_h1);
          OpenAjax.a11y.logger.debug("  h2: " + hi.nesting_h2);
          OpenAjax.a11y.logger.debug("  h3: " + hi.nesting_h3);
          OpenAjax.a11y.logger.debug("  h4: " + hi.nesting_h4);
          OpenAjax.a11y.logger.debug("  h5: " + hi.nesting_h5);
        }  
      }

      
      return li;
    }

    if ((tag_name == 'h2') || 
        (tag_name == 'h3') || 
        (tag_name == 'h4') || 
        (tag_name == 'h5') || 
        (tag_name == 'h6')) {
   
      he = new OpenAjax.a11y.cache.HeadingElement(dom_element, landmark_info, heading_info);    
  
      this.addHeadingElement(he);

      if (landmark_info.landmark_element) {
        landmark_info.landmark_element.addChildElement(he);
      } 
      else {
        this.addChildElement(he);  
      }

      if ((dom_element.computed_style.is_visible_to_at === OpenAjax.a11y.VISIBILITY.VISIBLE) &&
          he.name_for_comparison.length) {
      
        switch (tag_name) {
 
        case 'h2':
          heading_info.nesting_h2 = he;
          heading_info.nesting_h3 = null;
          heading_info.nesting_h4 = null;
          heading_info.nesting_h5 = null;

          if (li.landmark_element) {
        
            li.landmark_element.heading_info.nesting_h2 = he;
            li.landmark_element.heading_info.nesting_h3 = null;
            li.landmark_element.heading_info.nesting_h4 = null;
            li.landmark_element.heading_info.nesting_h5 = null;
          }  
          break;

        case 'h3':
          heading_info.nesting_h3 = he;
          heading_info.nesting_h4 = null;
          heading_info.nesting_h5 = null;

          if (li.landmark_element) {

            li.landmark_element.heading_info.nesting_h3 = he;
            li.landmark_element.heading_info.nesting_h4 = null;
            li.landmark_element.heading_info.nesting_h5 = null;
          }  
          break;

        case 'h4':
          heading_info.nesting_h4 = he;
          heading_info.nesting_h5 = null;
  
          if (li.landmark_element) {
            li.landmark_element.heading_info.nesting_h4 = he;
            li.landmark_element.heading_info.nesting_h5 = null;
          }  
          break;

        case 'h5':
          heading_info.nesting_h5 = he;
  
          if (li.landmark_element) {
            li.landmark_element.heading_info.nesting_h5 = he;
          }  
          break;
        
        default:
          break;
        }
      }

      return li;
    }
    
    if (tag_name == 'title' && !this.has_title) {
  
      me = new OpenAjax.a11y.cache.TitleElement(dom_element);    
   
      // There is only one title for a document, even when there are frames and iframes
      this.has_title = true;

      this.title_element = me;
    
      return li;
    }

    if (tag_name == 'body' && !this.page_element) {
  
      me = new OpenAjax.a11y.cache.PageElementHeadingsLandmarks(dom_element, li.main_element, this.title_element);    
   
      // There is only one body element for a document, even when there are frames and iframes
      this.page_element = me;
    
      return li;
    }
    
    
    
    // elements that contain rendered content without having child dom text nodes
    if ((tag_name == 'area')     ||
        (tag_name == 'canvas')   ||
        (tag_name == 'input')    ||
        (tag_name == 'img')      ||
        (tag_name == 'textarea') ||
        (tag_name == 'select')) {
        
      this.elements_with_content.push(dom_element);
      
      if (dom_element.parent_landmark) dom_element.parent_landmark.addToElementCount(1); 
      
      return li;
    }  

    // elements that may have rendered content without having child dom text nodes
    if ((tag_name == 'applet')   ||
        (tag_name == 'embed')    ||
        (tag_name == 'object')) {
      dom_element.may_have_renderable_content = true;  
      this.elements_with_content.push(dom_element);
      
//      OpenAjax.a11y.logger.debug("Parent Element: " +  dom_element.parent_landmark + " (" + dom_element + ")");

      if (dom_element.parent_landmark) dom_element.parent_landmark.addToElementCount(1); 
      
      return li;
    }  

    // Keep track of iframe elements
    if ((tag_name == 'iframe')) {
      this.iframe_elements.push(dom_element);
      li.iframe_element = dom_element;
      return li;
    }  

    // Keep track of body elements
    if ((tag_name == 'body')) {
      li.body_element = dom_element;
      return li;
    }  


  }
  else {
    tag_name = dom_element.parent_element.tag_name;
    if (tag_name != 'title'  && 
        tag_name != 'script' && 
        tag_name != 'style'  &&
        dom_element.text_length) {
      this.elements_with_content.push(dom_element);

//      OpenAjax.a11y.logger.debug("Parent Element: " +  dom_element.parent_landmark + " (" + dom_element + ")");

      if (dom_element.parent_landmark) dom_element.parent_landmark.addToElementCount(1); 

    }  
  }
  
  return li;
  
};

/**
 * @method traverseDOMElementsForLandmarkElements
 *
 * @memberOf OpenAjax.a11y.cache.HeadingsLandmarksCache
 *
 * @desc Traverses DOMElement objects in the tree to update the landmarks and headings cache 
 *
 * @param  {DOMElement}    dom_element - DOMElement object to check for inclusion in landmarks and headings cache
 * @param  {LandmarkInfo}  landmark_info  - Information about the current landmarks that are parents to this item
 * @param  {HeadingInfo}   heading_info   - Information about the current headers used for nesting rules
 */
 
OpenAjax.a11y.cache.HeadingsLandmarksCache.prototype.traverseDOMElementsForLandmarkElements = function (dom_element, landmark_info, heading_info) {

  if (!dom_element) return;

  if (dom_element.type == Node.ELEMENT_NODE) {

    var li = this.updateCacheItems(dom_element, landmark_info, heading_info);
    
    for (var i = 0; i < dom_element.child_dom_elements.length; i++ ) {
      this.traverseDOMElementsForLandmarkElements(dom_element.child_dom_elements[i], li);
    } 
  }
};


/**
 * @method updateCache
 *
 * @memberOf OpenAjax.a11y.cache.HeadingsLandmarksCache
 *
 * @desc Traverses the DOMElements to update the landmarks and heading cache
 *       NOTE: This function is only used when the specialized caches
 *       are build as rules need them.  In this condition, if the rules 
 *       dependent on the landmark and headings cache are disabled, this 
 *       cache would not be updated
 */
 
OpenAjax.a11y.cache.HeadingsLandmarksCache.prototype.updateCache = function () {
  var i;
  var li;
  var children = this.dom_cache.element_cache.child_dom_elements;
  var children_len = children.length;
 
  this.initCache();
 
  li = new OpenAjax.a11y.cache.LandmarkInfo(null);
  hi = new OpenAjax.a11y.cache.HeadingInfo(null);
 
  for (i=0; i < children_len; i++) {
    this.traverseDOMElementsForLandmarkElements(children[i], li, hi);
  }  

  this.up_to_date = true;
};


/**
 * @method sortLandmarkElements
 *
 * @memberOf OpenAjax.a11y.cache.HeadingsLandmarksCache
 *
 * @desc    Sorts the landmark elements list based on a property of the landmark element object
 *
 * @param   {String}   property   - LandmarkElement object property used to sort the cache
 * @param   {Boolean}  ascending  - true if sort in ascending order; false in descending order
 *
 * @return  {Boolean}  Return true if list was sorted; false was not sorted due to an error
 */

OpenAjax.a11y.cache.HeadingsLandmarksCache.prototype.sortLandmarkElements = function(property, ascending ) {

  var swapped = false;
  var temp = null;
  var i;

  if( this.landmark_elements && this.landmark_elements.length && !this.landmark_elements[0][property] ) {
    return false;
  } // endif

  var landmark_elements_len = this.landmark_elements.length;

  if( ascending ) {
    do{
      swapped = false;
      for (i = 1; i < landmark_elements_len; i++ ) {
        if (this.landmark_elements[i-1][property] > this.landmark_elements[i][property]) {
          // swap the values
          temp = this.landmark_elements[i-1];
          this.landmark_elements[i-1] = this.landmark_elements[i];
          this.landmark_elements[i] = temp;
          swapped = true;
        } 
      } // end loop
    } while (swapped);
  }
  else {
    do {
      swapped = false;
      for (i = 1; i < landmark_elements_len; i++) {
        if (this.landmark_elements[i-1][property] < this.landmark_elements[i][property]) {
          // swap the values
          temp = this.landmark_elements[i-1];
          this.landmark_elements[i-1] = this.landmark_elements[i];
          this.landmark_elements[i] = temp;
          swapped = true;
        } 
      } // end loop
    } while (swapped);
  } 

  this.landmark_sort_property = property;

  return true;

};

/**
 * @method sortHeadingElements
 *
 * @memberOf OpenAjax.a11y.cache.HeadingsLandmarksCache
 *
 * @desc  Sorts the heading_elements array based on a property of the HeadingElement object
 *
 * @param {String}   property   - HeadingElement object property used to sort the cache
 * @param {Boolean}  ascending  -  true if sort in ascending order; false in descending order
 *
 * @return  {Boolean}  true if list was sorted, false if not
 */

OpenAjax.a11y.cache.HeadingsLandmarksCache.prototype.sortHeadingElements = function(property, ascending ) {

  var swapped = false;
  var temp = null;
  var i;

  if( this.heading_elements && this.heading_elements.length && !this.heading_elements[0][property] ) {
    return false;
  } // endif

  var heading_elements_len = this.heading_elements.length;

  if( ascending ) {
    do{
      swapped = false;
      for (i = 1; i < heading_elements_len; i++ ) {
        if (this.heading_elements[i-1][property] > this.heading_elements[i][property]) {
          // swap the values
          temp = this.heading_elements[i-1];
          this.heading_elements[i-1] = this.heading_elements[i];
          this.heading_elements[i] = temp;
          swapped = true;
        } 
      } // end loop

    } while (swapped);
  }
  else {
    do {
      swapped = false;
      for (i = 1; i < heading_elements_len; i++) {
   
        if (this.heading_elements[i-1][property] < this.heading_elements[i][property]) {
          // swap the values
          temp = this.heading_elements[i-1];
          this.heading_elements[i-1] = this.heading_elements[i];
          this.heading_elements[i] = temp;
          swapped = true;
        } 
      } // end loop
    } while (swapped);
  } 

  this.heading_sort_property = property;

  return true;

};

/* ---------------------------------------------------------------- */
/*                       LandmarkElement                            */ 
/* ---------------------------------------------------------------- */

/**
 * @constructor LandmarkElement
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates a landmark element object used to hold information about a landmark 
 *
 * @param  {DOMelement}       dom_element      - The dom element object representing the landmark element 
 * @param  {LandmarkElement}  parent_landmark  - Information about the parent landmark (NOTE: can be null)
 * @param  {CacheElement}     body_element     - iframe or frame reference, this is useful for landmarks rules related to only one
 *
 * @property  {DOMElement}  dom_element     - Reference to the dom element representing the landmark element
 * @property  {String}      cache_id        - String that uniquely identifies the cache element object in the cache
 * @property  {Number}      document_order  - Ordinal position of the landmark element in the document in relationship to other landmark elements
 * @property  {String}      role            - String representing the type of landmark
 *
 * @property  {Array}  child_cache_elements  - Array of child cache landmark and heading element objects as part of cache landmark and header tree 
 *
 * @property  {LandmarkElement}  parent_landmark  - Information about the parent landmark (NOTE: can be null)
 * @property  {CacheElement}     body_element     - iframe or frame reference, this is useful for landmarks rules related to only one (NOTE: can be null)
 * 
 * @property  {String}   label                  - Accessible label of the landmark 
 * @property  {Number}   label_length           - Length of label text 
 * @property  {Number}   label_source           - Constant representing the source of the label (i.e. aria-label, aria-labelledby, title...) 
 * @property  {String}   label_for_comparison   - Accessible label for comparison (i.e. lowercase, trimmed and space normalized)
 */

OpenAjax.a11y.cache.LandmarkElement = function (dom_element, parent_landmark, body_element) {

  this.dom_element           = dom_element;
  this.cache_id              = "";
  this.document_order        = 0;
  this.role                  = dom_element.role;

  
  var hi = new OpenAjax.a11y.cache.HeadingInfo(null);
  this.heading_info = hi;

  this.child_cache_elements  = [];

  this.parent_landmark       = parent_landmark;
  this.body_element          = body_element;
 
  this.computed_label                 = "";
  this.computed_label_length          = 0;
  this.computed_label_source          = OpenAjax.a11y.SOURCE.NONE;
  this.computed_label_for_comparison  = "";
  this.accessible_name                = "";
  
  this.elements_with_content_count = 0;

};

/**
 * @method addChildElement
 *
 * @memberOf OpenAjax.a11y.cache.LandmarkElement
 *
 * @desc Adds a LandmarkElement or HeaderElement object to the tree of landmark/heading elements  
 *
 * @param {LandmarkElement | HeadingElement}  child_element  - Landmark element or heading element object to add 
 */

OpenAjax.a11y.cache.LandmarkElement.prototype.addChildElement = function (child_element) {

  if (child_element) {
    this.child_cache_elements.push(child_element); 
  }  

}; 


/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.LandmarkElement
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.LandmarkElement.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method addToElementCount
 *
 * @memberOf OpenAjax.a11y.cache.LandmarkElement
 *
 * @desc Adds N elements to the count of elements with content
 *
 * @param {Number}  n  - Number of elements to add to count
 */

OpenAjax.a11y.cache.LandmarkElement.prototype.addToElementCount = function (n) {

  if (n > 0) this.elements_with_content_count += n;

};


/**
 * @method getElementsWithContentCount
 *
 * @memberOf OpenAjax.a11y.cache.LandmarkElement
 *
 * @desc Get the number of child elements with content, inlcuding the elements with 
 *       content of child landmark elements 
 *
 * @return {Number}  Number of elements with content 
 */

OpenAjax.a11y.cache.LandmarkElement.prototype.getElementsWithContentCount = function () {
    
  var count = this.elements_with_content_count;
    
  var child_elements     = this.child_cache_elements;
  var child_elements_len = child_elements.length;
      
  for (var i = 0; i < child_elements_len; i++) {
      
    var cle = child_elements[i];
      
    if (typeof cle.getElementsWithContentCount === 'object') {
      count += cle.getElementsWithContentCount();
    }  
  }
    
  return count;
};

/**
 * @method getHeadings
 *
 * @memberOf OpenAjax.a11y.cache.LandmarkElement
 *
 * @desc Get all the heading elements in a landmark
 *
 * @return {Array}  Array of heading elements 
 */

OpenAjax.a11y.cache.LandmarkElement.prototype.getHeadings = function () {
    
  var headings = [];
    
  var child_elements     = this.child_cache_elements;
  var child_elements_len = child_elements.length;
      
  for (var i = 0; i < child_elements_len; i++) {
      
    var cle = child_elements[i];
      
    if (typeof cle.level === 'number') headings.push(cle);
    else if (typeof cle.getHeadings === 'object') headings = headings.concat(cle.getHeadings());
  }
    
  return headings;
};


/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.LandmarkElement
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style display objects
 */

OpenAjax.a11y.cache.LandmarkElement.prototype.getStyle = function () {

  return this.dom_element.getStyle();
  
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.LandmarkElement
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of attribute display object
 */

OpenAjax.a11y.cache.LandmarkElement.prototype.getAttributes = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  var attributes = this.dom_element.getAttributes();
  
//  cache_nls.addPropertyIfDefined(attributes, this, 'tag_name');
  
  if (!unsorted) this.dom_element.sortItems(attributes);
  
  return attributes;
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.LandmarkElement
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of cache property display object
 */

OpenAjax.a11y.cache.LandmarkElement.prototype.getCacheProperties = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;

  var properties = this.dom_element.getCacheProperties(unsorted);

  cache_nls.addPropertyIfDefined(properties, this, 'computed_label');
  cache_nls.addPropertyIfDefined(properties, this, 'computed_label_source');
  cache_nls.addPropertyIfDefined(properties, this, 'computed_label_for_comparison');
  cache_nls.addPropertyIfDefined(properties, this, 'accessible_name');
  cache_nls.addPropertyIfDefined(properties, this, 'parent_landmark');
  cache_nls.addPropertyIfDefined(properties, this, 'elements_with_content_count');

  if (!unsorted) this.dom_element.sortItems(properties);

  return properties;
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.LandmarkElement
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.LandmarkElement.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};


/**
 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.LandmarkElement
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event item display objects
 */

OpenAjax.a11y.cache.LandmarkElement.prototype.getEvents = function () {
   
  return this.dom_element.getEvents();
  
};

/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.LandmarkElement
 *
 * @desc Returns a text string representation of the landmark element 
 *
 * @return {String} Returns string represention the landmark element object
 */
 
OpenAjax.a11y.cache.LandmarkElement.prototype.toString = function () {
 var de = this.dom_element;
 if (this.accessible_name && this.accessible_name.length) return de.tag_name + "[" + de.role + "]: " + this.accessible_name;  
 return de.tag_name + "[" + de.role + "]";   
}; 

/* ---------------------------------------------------------------- */
/*                       HeadingElement                             */ 
/* ---------------------------------------------------------------- */

/**
 * @constructor HeadingElement
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates a heading element object used to hold information about a h1 - h6 heading elements 
 *
 * @param  {DOMelement}       dom_element      - The dom element object representing the heading element 
 *
 * @property  {DOMElement}  dom_element     - Reference to the dom element representing the optgroup element
 * @property  {String}      cache_id        - String that uniquely identifies the cache element object in the cache
 * @property  {Number}      document_order  - Ordinal position of the heading element in the document in relationship to other heading elements
 *
 * @property  {Object}  landmark_info       - Landmark information object 
 * @property  {Object}  heading_info        - heading information object 
 * @property  {Number}  level               - Level of the heading
 *
 * @property  {String}   name                  - Calculated accessible name of the heading 
 * @property  {Number}   name_length           - Length of accessible name 
 * @property  {String}   name_for_comparison   - Accessible name for comparison (i.e. lowercase, trimmed and space normalized)
 * @property  {String}   name_from_text_nodes  - Accessible name content from text nodes
 * @property  {String}   name_from_image_alt   - Accessible name content from alt content of images
 * @property  {Number}   image_count           - Number of images that are descendents of the link
 * @property  {Boolean}  text_only_from_image  - true if accessble name is only from an image, otherwise false 
 * @property  {Boolean}  is_visible            - true if element is visible to AT and/or screen, otherwise false 
 * @property  {Boolean}  has_content           - true if element has content, otherwise false 
 *
 * @property  {Heading Element}  nesting_parent_heading   - "parent" heading viewed from a document perspective (i.e. ignore landmarks), can be null 
 * @property  {Heading Element}  landmark_parent_heading  - "parent" heading in current landmark, can be null 
 */

OpenAjax.a11y.cache.HeadingElement = function (dom_element, landmark_info, heading_info) {

 
  this.dom_element     = dom_element;
  this.cache_id        = "";
  this.document_order  = 0;

  var le = landmark_info.landmark_element;

  this.parent_landmark =  le;
  
  this.last_parent_heading          = null;
  this.nesting_parent_heading       = null;
  this.last_landmark_parent_heading = null;
  this.landmark_parent_heading      = null;
  this.is_past_first_h1        = heading_info.is_past_first_h1;

  var ano = dom_element.getTextObject(true);  // text content must be visible
  
  this.name                  = ano.name;
  this.name_length           = ano.name.length;
  this.name_for_comparison   = ano.name.normalizeSpace().toLowerCase();
  this.name_from_text_nodes  = ano.name_from_text_nodes;
  this.name_from_image_alt   = ano.name_from_image_alt;
  this.image_count           = ano.image_count;
  this.text_only_from_image  = (ano.name_from_text_nodes.length === 0) && (ano.name_from_image_alt.length > 0);
  
  var is_visible = dom_element.computed_style.is_visible_to_at === OpenAjax.a11y.VISIBILITY.VISIBLE;
  var has_content = this.name_for_comparison.length > 0;
  
  this.is_visible  = is_visible;
  this.has_content = has_content;
   
  switch( dom_element.tag_name) {

  case 'h1':
    this.level = 1;
    
    if (is_visible && has_content) {     
    
      this.last_parent_heading          = null;
      this.nesting_parent_heading       = null;
    
      if (le) {
        this.last_landmark_parent_heading = null;
        this.landmark_parent_heading      = null;
      }  
    }
    break;
    
  case 'h2':
    this.level = 2;
    
    if (is_visible && has_content) {     
    
      this.last_parent_heading          = heading_info.nesting_h1;
      this.nesting_parent_heading       = heading_info.nesting_h1;
    
      if (le) {
        this.last_landmark_parent_heading = le.heading_info.nesting_h1;
        this.landmark_parent_heading      = le.heading_info.nesting_h1;
      }  
    }
    
    break;
    
  case 'h3':
    this.level = 3;
    
    if (is_visible && has_content) {     
      this.last_parent_heading     = heading_info.nesting_h2;
      this.nesting_parent_heading  = heading_info.nesting_h2;
    
      if (this.last_parent_heading === null) this.last_parent_heading = heading_info.nesting_h1;
    
      if (le) {
        this.landmark_parent_heading      = le.heading_info.nesting_h2;    
        this.last_landmark_parent_heading = le.heading_info.nesting_h2;

        if (this.last_landmark_parent_heading === null) this.last_landmark_parent_heading = le.heading_info.nesting_h1;
      }
    }
    
    break;
    
  case 'h4':
    this.level = 4;
    
    if (is_visible && has_content) {     
      this.last_parent_heading     = heading_info.nesting_h3;
      this.nesting_parent_heading  = heading_info.nesting_h3;
    
      if (this.last_parent_heading === null) this.last_parent_heading = heading_info.nesting_h2;
      if (this.last_parent_heading === null) this.last_parent_heading = heading_info.nesting_h1;

      if (le) {
        this.landmark_parent_heading      = le.heading_info.nesting_h3;
        this.last_landmark_parent_heading = le.heading_info.nesting_h3;
     
        if (this.last_landmark_parent_heading === null) this.last_landmark_parent_heading = le.heading_info.nesting_h2;
        if (this.last_landmark_parent_heading === null) this.last_landmark_parent_heading = le.heading_info.nesting_h1;
      }
    }
    
    break;
    
  case 'h5':
    this.level = 5;
    
    if (is_visible && has_content) {     
      this.last_parent_heading     = heading_info.nesting_h4;
      this.nesting_parent_heading  = heading_info.nesting_h4;
    
      if (this.last_parent_heading === null) this.last_parent_heading = heading_info.nesting_h3;
      if (this.last_parent_heading === null) this.last_parent_heading = heading_info.nesting_h2;
      if (this.last_parent_heading === null) this.last_parent_heading = heading_info.nesting_h1;

      if (le) {
        this.landmark_parent_heading      = le.heading_info.nesting_h4;
        this.last_landmark_parent_heading = le.heading_info.nesting_h4;
    
        if (this.last_landmark_parent_heading === null) this.last_landmark_parent_heading = le.heading_info.nesting_h3;
        if (this.last_landmark_parent_heading === null) this.last_landmark_parent_heading = le.heading_info.nesting_h2;
        if (this.last_landmark_parent_heading === null) this.last_landmark_parent_heading = le.heading_info.nesting_h1;
      }
    }  
    break;
    
  case 'h6':
    this.level = 6;
    
    if (is_visible && has_content) {     
      this.last_parent_heading     = heading_info.nesting_h5;
      this.nesting_parent_heading  = heading_info.nesting_h5;
    
      if (this.last_parent_heading === null) this.last_parent_heading = heading_info.nesting_h4;
      if (this.last_parent_heading === null) this.last_parent_heading = heading_info.nesting_h3;
      if (this.last_parent_heading === null) this.last_parent_heading = heading_info.nesting_h2;
      if (this.last_parent_heading === null) this.last_parent_heading = heading_info.nesting_h1;

      if (le) {
        this.landmark_parent_heading      = le.heading_info.nesting_h5;
        this.last_landmark_parent_heading = le.heading_info.nesting_h5;
    
        if (this.last_landmark_parent_heading === null) this.last_landmark_parent_heading = le.heading_info.nesting_h4;
        if (this.last_landmark_parent_heading === null) this.last_landmark_parent_heading = le.heading_info.nesting_h3;
        if (this.last_landmark_parent_heading === null) this.last_landmark_parent_heading = le.heading_info.nesting_h2;
        if (this.last_landmark_parent_heading === null) this.last_landmark_parent_heading = le.heading_info.nesting_h1;
      }
    }
    
    break;
    
  default:
    this.level = 0;   
    break;
  } // end switch

  return this;
    
};


/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.HeadingElement
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.HeadingElement.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.HeadingElement
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style display objects
 */

OpenAjax.a11y.cache.HeadingElement.prototype.getStyle = function () {

  return this.dom_element.getStyle();
  
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.HeadingElement
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of attribute display object
 */

OpenAjax.a11y.cache.HeadingElement.prototype.getAttributes = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  var attributes = this.dom_element.getAttributes();
  
//  cache_nls.addPropertyIfDefined(attributes, this, 'tag_name');
  
  if (!unsorted) this.dom_element.sortItems(attributes);
  
  return attributes;
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.HeadingElement
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of cache property display object
 */

OpenAjax.a11y.cache.HeadingElement.prototype.getCacheProperties = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;

  var properties = this.dom_element.getCacheProperties(unsorted);

 cache_nls.addPropertyIfDefined(properties, this, 'name');
 cache_nls.addPropertyIfDefined(properties, this, 'name_for_comparison');
 cache_nls.addPropertyIfDefined(properties, this, 'name_from_text_nodes');
 cache_nls.addPropertyIfDefined(properties, this, 'name_from_image_alt');
 cache_nls.addPropertyIfDefined(properties, this, 'image_count');
 cache_nls.addPropertyIfDefined(properties, this, 'text_only_from_image');
 cache_nls.addPropertyIfDefined(properties, this, 'parent_landmark');
 
 cache_nls.addPropertyIfDefined(properties, this, 'level');
 cache_nls.addPropertyIfDefined(properties, this, 'is_past_first_h1');
 cache_nls.addPropertyIfDefined(properties, this, 'nesting_parent_heading');
 cache_nls.addPropertyIfDefined(properties, this, 'last_parent_heading');
 cache_nls.addPropertyIfDefined(properties, this, 'landmark_parent_heading');
 cache_nls.addPropertyIfDefined(properties, this, 'last_landmark_parent_heading');
 
  if (!unsorted) this.dom_element.sortItems(properties);

  return properties;
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.HeadingElement
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.HeadingElement.prototype.getCachePropertyValue = function (property) {

//  OpenAjax.a11y.logger.debug("Heading property: " + property + " value= " + this[property]);

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
  
};

/**

 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.HeadingElement
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event item display objects
 */

OpenAjax.a11y.cache.HeadingElement.prototype.getEvents = function () {
   
  return this.dom_element.getEvents();
  
};


/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.HeadingElement
 *
 * @desc Returns a text string representation of the heading (h1-h6) element 
 *
 * @return {String} Returns string represention the heading element object
 */
  
OpenAjax.a11y.cache.HeadingElement.prototype.toString = function() {
  if (this.name && this.name_length) {
    return "h" + this.level + ": " + this.name;
  }
  else {
    return "h" + this.level + ": no content";
  }

};

/* ---------------------------------------------------------------- */
/*                        MainElement                               */ 
/* ---------------------------------------------------------------- */

/**
 * @constructor MainElement
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates a main landmark element object used to hold information about a main landmark 
 *
 * @param  {DOMElement}   dom_element      - The dom element object representing the landmark element 
 * @param  {MainElement}  parent_landmark  - Information about the parent landmark (NOTE: can be null)
 *
 * @property  {DOMElement}   dom_element      - Reference to the dom element representing the main landmark element
 * @property  {String}       cache_id         - String that uniquely identifies the cache element object in the cache
 * @property  {Number}       document_order   - Ordinal position of the title and main cache items in the document to other title and main cache items
 * @property  {String}       role             - String identifying the landmark as "main"
 *
 * @property  {LandmarkElement}  parent_landmark  - Information about the parent landmark (NOTE: can be null)
 * @property  {CacheElement}     body_element     - iframe or frame reference, this is useful for landmarks rules related to only one (NOTE: can be null)
 * 
 * @property  {Array}  child_cache_elements  - List of child cache title element, main landmarks and h1 heading element objects as part of cache title and main elements tree 
 *
 * @property  {Array}   h1_elements  -  List of all the h1 heading elements that are children of the main landmark
 * @property  {Number}  type         -  Constant representing the type of main landmark
 *
 * @property  {String}   label                  - Accessible label of the landmark 
 * @property  {Number}   label_length           - Length of label text 
 * @property  {Number}   label_source           - Constant representing the source of the label (i.e. aria-label, aria-labelledby, title...) 
 * @property  {String}   label_for_comparison   - Accessible label for comparison (i.e. lowercase, trimmed and space normalized)
 */

OpenAjax.a11y.cache.MainElement = function (dom_element, parent_landmark, body_element) {

  this.dom_element     = dom_element;
  this.cache_id        = "";  
  this.document_order  = 0;
  this.role            = "main";
  
  var hi = new OpenAjax.a11y.cache.HeadingInfo(null);
  this.heading_info = hi;
  

  this.child_cache_elements = [];
  this.h1_elements          = [];
  this.main_type            = OpenAjax.a11y.MAIN.ROLE_MAIN;
  
  this.parent_landmark = parent_landmark; // restricted to main landmarks
  this.body_element    = body_element;
  
 
  this.computed_label                 = "";
  this.computed_label_length          = 0;
  this.computed_label_source          = OpenAjax.a11y.SOURCE.NONE;
  this.computed_label_for_comparison  = "";
  this.accessible_name                = "";
  
  this.elements_with_content_count = 0;

}; 


/**
 * @method addChildElement
 *
 * @memberOf OpenAjax.a11y.cache.MainElement
 *
 * @desc Adds a child landmark or heading object to the tree of landmarks and heading elements  
 *
 * @param {Object}  cache_element  -  landmark or heading element object to add to the tree
 */

OpenAjax.a11y.cache.MainElement.prototype.addChildElement = function (cache_element) {

  if (cache_element) {
    this.child_cache_elements.push(cache_element); 
  }  

}; 

/**
 * @method addH1Element
 *
 * @memberOf OpenAjax.a11y.cache.MainElement
 *
 * @desc Adds a H1 element to the list of H1 elements that are a child elements of the main content   
 *
 * @param {H1Element}  h1_element  -  H1 element object to add to list 
 */

OpenAjax.a11y.cache.MainElement.prototype.addH1Element = function (h1_element) {

  if (h1_element) {
    this.h1_elements.push(h1_element); 
  }  

}; 


/**
 * @method addToElementCount
 *
 * @memberOf OpenAjax.a11y.cache.MainElement
 *
 * @desc Adds N elements to the count of elements with content
 *
 * @param {Number}  n  - Number of elements to add to count
 */

OpenAjax.a11y.cache.MainElement.prototype.addToElementCount = function (n) {

  if (n > 0) this.elements_with_content_count += n;

};

 /**
 * @method getElementsWithContentCount
 *
 * @memberOf OpenAjax.a11y.cache.MainElement
 *
 * @desc Get the number of child elements with content, inlcuding the elements with 
 *       content of child landmark elements 
 *
 * @return {Number}  Number of elements with content 
 */

OpenAjax.a11y.cache.MainElement.prototype.getElementsWithContentCount = function () {
    
  var count = this.elements_with_content_count;
    
  var child_elements     = this.child_cache_elements;
  var child_elements_len = child_elements.length;
      
  for (var i = 0; i < child_elements_len; i++) {
      
    var cle = child_elements[i];
      
    if (typeof cle.getElementsWithContentCount === 'object') {
      count += cle.getElementsWithContentCount();
    }  
  }
    
  return count;
};

 /**
 * @method getHeadings
 *
 * @memberOf OpenAjax.a11y.cache.MainElement
 *
 * @desc Get all the heading elements in a landmark
 *
 * @return {Array}  Array of heading elements 
 */

OpenAjax.a11y.cache.MainElement.prototype.getHeadings = function () {
    
  var headings = [];
    
  var child_elements     = this.child_cache_elements;
  var child_elements_len = child_elements.length;
      
  for (var i = 0; i < child_elements_len; i++) {
      
    var cle = child_elements[i];
      
    if (typeof cle.level === 'number') headings.push(cle);
    else if (typeof cle.getHeadings === 'object') headings = headings.concat(cle.getHeadings());
  }
    
  return headings;
};

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.MainElement
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.MainElement.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.MainElement
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style display objects
 */

OpenAjax.a11y.cache.MainElement.prototype.getStyle = function () {

  return this.dom_element.getStyle();
  
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.MainElement
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of attribute display object
 */

OpenAjax.a11y.cache.MainElement.prototype.getAttributes = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  var attributes = this.dom_element.getAttributes();
  
//  cache_nls.addPropertyIfDefined(attributes, this, 'tag_name');
  
  if (!unsorted) this.dom_element.sortItems(attributes);
  
  return attributes;
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.MainElement
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of cache property display object
 */

OpenAjax.a11y.cache.MainElement.prototype.getCacheProperties = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;

  var properties = this.dom_element.getCacheProperties(unsorted);

  cache_nls.addPropertyIfDefined(properties, this, 'computed_label');
  cache_nls.addPropertyIfDefined(properties, this, 'computed_label_source');
  cache_nls.addPropertyIfDefined(properties, this, 'computed_label_for_comparison');
  cache_nls.addPropertyIfDefined(properties, this, 'accessible_name');
  cache_nls.addPropertyIfDefined(properties, this, 'parent_landmark');
  cache_nls.addPropertyIfDefined(properties, this, 'elements_with_content_count');
  
  if (!unsorted) this.dom_element.sortItems(properties);

  return properties;
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.MainElement
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.MainElement.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};


/**

 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.MainElement
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event item display objects
 */

OpenAjax.a11y.cache.MainElement.prototype.getEvents = function () {
   
  return this.dom_element.getEvents();
  
};

/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.MainElement
 *
 * @desc Returns a text string representation of the main landmark element 
 *
 * @return {String} Returns string represention the landmark element object
 */
  
OpenAjax.a11y.cache.MainElement.prototype.toString = function () {
  if (this.accessible_name && this.accessible_name.length) return this.dom_element.tag_name + "[main]: " + this.accessible_name;  
  return this.dom_element.tag_name + "[main]";  
};

/* ---------------------------------------------------------------- */
/*                         H1Element                                */ 
/* ---------------------------------------------------------------- */

/**
 * @constructor H1Element
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates a h1 heading element object used to hold information about a h1 heading elements used for titling 
 *
 * @param  {DOMelement}       dom_element      - The dom element object representing the heading element 
 * @param  {LandmarkElement}  parent_landmark  - Information about the parent landmark (NOTE: can be null)
 * @param  {MainElement}      main_landmark    - Information about the parent main landmark (NOTE: can be null)
 *
 * @property  {DOMElement}   dom_element      - Reference to the dom element representing the optgroup element
 * @property  {String}       cache_id         - String that uniquely identifies the cache element object in the cache
 * @property  {Number}       document_order   - Ordinal position of the title and main cache items in the document to other title and main cache items
 *
 * @property  {LandmarkElement}  parent_landmark  - Information about the parent landmark (NOTE: can be null)
 * @property  {MainElement}      main_landmark    - Information about the parent main landmark (NOTE: can be null)
 *
 * @property  {Array}  child_cache_elements  - List of child cache title element, main landmarks and h1 heading element objects as part of cache title and main elements tree  
 *
 * @property  {Number}   type               -  Constant representing the type of main landmark
 * @property  {Boolean}  is_label_for_main  - true if h1 is being used as a label for main landmark, otherwise false
 * @property  {Boolean}  is_child_of_main   - true if h1 is the child of the main landmark it is a label for, otherwise false
 * @property  {Boolean}  text_only_from_image  - true if accessble name is only from an image, otherwise false 
 * @property  {Boolean}  is_visible            - true if element is visible to AT and/or screen, otherwise false 
 * @property  {Boolean}  has_content           - true if element has content, otherwise false 
 *
 * @property  {String}   name                  - Calculated accessible name of the heading 
 * @property  {Number}   name_length           - Length of accessible name 
 * @property  {String}   name_for_comparison   - Accessible name for comparison (i.e. lowercase, trimmed and space normalized)
 */

OpenAjax.a11y.cache.H1Element = function (dom_element, parent_landmark, main_landmark) {

  this.dom_element     = dom_element;
  this.cache_id        = "";  
  this.document_order  = 0;
  
  this.parent_landmark  = parent_landmark; // restricted to main landmarks
  this.main_landmark    = main_landmark;   // restricted to main landmarks
  this.child_cache_elements = [];   // The child array is always empty for an H1Element
  this.level = 1;

  
  this.main_type            = OpenAjax.a11y.MAIN.H1_ELEMENT;
  this.is_label_for_main    = false;
  
  if (main_landmark) this.is_child_of_main = true;
  else this.is_child_of_main     = false;

  var ano = dom_element.getTextObject(true);  // text content must be visible
  
  this.name                  = ano.name;
  this.name_length           = ano.name.length;
  this.name_for_comparison   = ano.name.normalizeSpace().toLowerCase();
  this.name_from_text_nodes  = ano.name_from_text_nodes;
  this.name_from_image_alt   = ano.name_from_image_alt;
  this.image_count           = ano.image_count;
  this.text_only_from_image  = (ano.name_from_text_nodes.length === 0) && (ano.name_from_image_alt.length > 0);
  
  var is_visible = dom_element.computed_style.is_visible_to_at === OpenAjax.a11y.VISIBILITY.VISIBLE;
  var has_content = this.name_for_comparison.length > 0;
  
  this.is_visible  = is_visible;
  this.has_content = has_content;

  if (is_visible && has_content) {     
    
    this.last_parent_heading          = null;
    this.nesting_parent_heading       = null;
    
    this.last_landmark_parent_heading = null;
    this.landmark_parent_heading      = null;
  }

  
}; 

/**
 * @method isH1UsedAsLabelForMainRole
 *
 * @memberOf OpenAjax.a11y.cache.H1Element
 * 
 * @desc  Determines if an H1 element is being used as a label for a main Role
 *
 * @return  {Boolean}  True if the h1 element is being used as a label for the main landmark it is contained in, otherwise false
 */

OpenAjax.a11y.cache.H1Element.prototype.isH1UsedAsLabelForMainRole = function () {

  if (this.dom_element.id.length === 0 ||
      this.main_landmark === null) {
    this.is_label_for_main = false;  
    return;
  }  

  var me = this.main_landmark;
  var de = me.dom_element;

  if (de.aria_labelledby && de.aria_labelledby.indexOf(this.dom_element.id) >= 0) {
    this.is_label_for_main = true;   
  }
      
};

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.H1Element
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.H1Element.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.H1Element
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style display objects
 */

OpenAjax.a11y.cache.H1Element.prototype.getStyle = function () {

  return this.dom_element.getStyle();
  
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.H1Element
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of attribute display object
 */

OpenAjax.a11y.cache.H1Element.prototype.getAttributes = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  var attributes = this.dom_element.getAttributes();
  
//  cache_nls.addPropertyIfDefined(attributes, this, 'tag_name');
  
  if (!unsorted) this.dom_element.sortItems(attributes);
  
  return attributes;
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.H1Element
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of cache property display object
 */

OpenAjax.a11y.cache.H1Element.prototype.getCacheProperties = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;

  var properties = this.dom_element.getCacheProperties(unsorted);

 cache_nls.addPropertyIfDefined(properties, this, 'main_type');
 cache_nls.addPropertyIfDefined(properties, this, 'name');
 cache_nls.addPropertyIfDefined(properties, this, 'name_for_comparison');
 cache_nls.addPropertyIfDefined(properties, this, 'name_from_text_nodes');
 cache_nls.addPropertyIfDefined(properties, this, 'name_from_image_alt');
 cache_nls.addPropertyIfDefined(properties, this, 'image_count');
 cache_nls.addPropertyIfDefined(properties, this, 'text_only_from_image');
 cache_nls.addPropertyIfDefined(properties, this, 'is_label_for_main');
 cache_nls.addPropertyIfDefined(properties, this, 'is_child_of_main');
 
  if (!unsorted) this.dom_element.sortItems(properties);

  return properties;
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.H1Element
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.H1Element.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};

/**
 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.H1Element
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event item display objects
 */

OpenAjax.a11y.cache.H1Element.prototype.getEvents = function () {
   
  return this.dom_element.getEvents();
  
};


/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.H1Element
 *
 * @desc Returns a text string representation of the h1 heading element 
 *
 * @return {String} Returns string represention the h1 heading element object
 */
  
OpenAjax.a11y.cache.H1Element.prototype.toString = function () {
  if (this.name && this.name.length) return "h1: " + this.name; 
  else return "h1: no content";
};

/* ---------------------------------------------------------------- */
/*                       TitleElement                               */ 
/* ---------------------------------------------------------------- */

/**
 * @constructor TitleElement
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates a title element object used to hold information about a title element
 *
 * @param  {DOMelement}   dom_element      - The dom element object representing the heading element 
 * @param  {MainElement}  parent_landmark  - Information about the parent landmark (NOTE: can be null)
 *
 * @property  {DOMElement}   dom_element      - Reference to the dom element representing the optgroup element
 * @property  {String}       cache_id         - String that uniquely identifies the cache element object in the cache
 * @property  {Number}       document_order   - Ordinal position of the title and main cache items in the document to other title and main cache items
 *
 * @property  {Number}   type  -  Constant representing the title element 
 *
 * @property  {String}   name                  - Calculated accessible name of the heading 
 * @property  {Number}   name_length           - Length of accessible name 
 * @property  {String}   name_for_comparison   - Accessible name for comparison (i.e. lowercase, trimmed and space normalized)
 */

OpenAjax.a11y.cache.TitleElement = function (dom_element) {

  this.dom_element     = dom_element;
  this.cache_id        = "title";
  this.document_order  = 0;

  this.main_type          = OpenAjax.a11y.MAIN.TITLE_ELEMENT;

  this.name                 = dom_element.getText();
  this.name_length          = this.name.length;
  this.name_for_comparison  = this.name.normalizeSpace().toLowerCase();

  // these can probably be removed some day
  this.parent_landmark      = null; // restricted to main landmarks
  this.child_cache_elements = [];  // This array is always empty for the title element

}; 

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.TitleElement
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.TitleElement.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.TitleElement
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style display objects
 */

OpenAjax.a11y.cache.TitleElement.prototype.getStyle = function () {

  return this.dom_element.getStyle();
  
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.TitleElement
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of attribute display object
 */

OpenAjax.a11y.cache.TitleElement.prototype.getAttributes = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  var attributes = this.dom_element.getAttributes();
  
//  cache_nls.addPropertyIfDefined(attributes, this, 'tag_name');
  
  if (!unsorted) this.dom_element.sortItems(attributes);
  
  return attributes;
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.TitleElement
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of cache property display object
 */

OpenAjax.a11y.cache.TitleElement.prototype.getCacheProperties = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;

  var properties = this.dom_element.getCacheProperties(unsorted);

 cache_nls.addPropertyIfDefined(properties, this, 'name');
 cache_nls.addPropertyIfDefined(properties, this, 'name_for_comparison');
 cache_nls.addPropertyIfDefined(properties, this, 'main_type');
 
  if (!unsorted) this.dom_element.sortItems(properties);

  return properties;
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.TitleElement
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.TitleElement.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};



/**
 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.TitleElement
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event item display objects
 */

OpenAjax.a11y.cache.TitleElement.prototype.getEvents = function () {
   
  return this.dom_element.getEvents();
  
};

/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.TitleElement
 *
 * @desc Returns a text string representation of the title element 
 *
 * @return {String} Returns string represention the title element object
 */
  
OpenAjax.a11y.cache.TitleElement.prototype.toString = function () {
  var str = "title: ";
  
  if (this.name.length) str += this.name;  
  else str += 'no content';
  
  return str;
};


/* ---------------------------------------------------------------- */
/*                       PageElementHeadingsLandmarks                               */ 
/* ---------------------------------------------------------------- */

/**
 * @constructor PageElementHeadingsLandmarks
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates a body element object used to hold information about a title element
 *
 * @param  {DOMelement}   dom_element      - The dom element object representing the heading element 
 * @param  {MainElement}  parent_landmark  - This is always null since this is the root element
 *
 * @property  {DOMElement}   dom_element      - Reference to the dom element representing the optgroup element
 * @property  {String}       cache_id         - String that uniquely identifies the cache element object in the cache
 * @property  {Number}       document_order   - Ordinal position of the title and main cache items in the document to other title and main cache items
 *
 * @property  {MainElement}  parent_landmark  - Information about the parent main landmark (NOTE: can be null)
 * @property  {TitleElement} title_element    - Reference to title element (NOTE: can be null)
 *
 * @property  {Array}  child_cache_elements  - List of child cache title element, main landmarks and h1 heading element objects as part of cache title and main elements tree  
 *
 * @property  {Number}   type  -  Constant representing the body element 
 *
 */

OpenAjax.a11y.cache.PageElementHeadingsLandmarks = function (dom_element, parent_landmark, title_element) {

  this.dom_element     = dom_element;
  this.cache_id        = "page_heading";
  this.document_order  = 0;
  this.is_page_element = true;
  this.title_element   = title_element;

  this.main_type          = OpenAjax.a11y.MAIN.BODY_ELEMENT;

  this.child_cache_elements = []; // this is always empty for the body element

  this.parent_landmark    = parent_landmark; // restricted to main landmarks
  
  this.num_main_landmarks = 0;          // are defined in landmark rules
  this.num_visible_main_landmarks = 0;  // are defined in landmark rules
  
}; 

/**
 * @method addChildMainElement
 *
 * @memberOf OpenAjax.a11y.cache.PageElementHeadingsLandmarks
 *
 * @desc Adds a main landmark  object to the tree of title and main elements  
 *
 * @param {MainElement}  main_element  -  Main landmark element object to add 
 */

OpenAjax.a11y.cache.PageElementHeadingsLandmarks.prototype.addChildMainElement = function (main_element) {

  if (main_element) {
    this.child_cache_elements.push(main_element); 
  }  

};

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.PageElementHeadingsLandmarks
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.PageElementHeadingsLandmarks.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.PageElementHeadingsLandmarks
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style display objects
 */

OpenAjax.a11y.cache.PageElementHeadingsLandmarks.prototype.getStyle = function () {

  return this.dom_element.getStyle();
  
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.PageElementHeadingsLandmarks
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of attribute display object
 */

OpenAjax.a11y.cache.PageElementHeadingsLandmarks.prototype.getAttributes = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  var attributes = this.dom_element.getAttributes();
  
//  cache_nls.addPropertyIfDefined(attributes, this, 'tag_name');
  
  if (!unsorted) this.dom_element.sortItems(attributes);
  
  return attributes;
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.PageElementHeadingsLandmarks
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of cache property display object
 */

OpenAjax.a11y.cache.PageElementHeadingsLandmarks.prototype.getCacheProperties = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;

  var properties = this.dom_element.getCacheProperties(unsorted);
 
  if (!unsorted) this.dom_element.sortItems(properties);

  return properties;
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.PageElementHeadingsLandmarks
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.PageElementHeadingsLandmarks.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};



/**
 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.PageElementHeadingsLandmarks
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event item display objects
 */

OpenAjax.a11y.cache.PageElementHeadingsLandmarks.prototype.getEvents = function () {
   
  return this.dom_element.getEvents();
  
};

/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.PageElementHeadingsLandmarks
 *
 * @desc Returns a text string representation of the title element 
 *
 * @return {String} Returns string represention the title element object
 */
  
OpenAjax.a11y.cache.PageElementHeadingsLandmarks.prototype.toString = function () {

  if (this.title_element) return "page: " + this.title_element.name; 
  
  return "page: no page title";
};
/*
 * Copyright 2011-2013 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* ---------------------------------------------------------------- */
/*                            ImageCache                            */
/* ---------------------------------------------------------------- */

/**
 * @constructor ImagesCache
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates cache object representing information related to images in a document
 *
 * @param {DOMCache}   dom_cache   - Reference to the DOMCache object 
 * 
 * @property {DOMCache} dom_cache  - Reference to the DOMCache object 
 *         
 * @property {Boolean}  up_to_date - Boolean true if the cache has been creating using the current DOMElements, else false
 *                                   NOTE: This is a common property of all caches and is used when selectively build caches 
 *                                         based on whether a rule needs the cache
 *
 * @property {Array}    image_elements  - List of image element objects in the document 
 * @property {Number}   length          - Number of image element objects in the list 
 *
 * @property {String}   sort_property   - Image element object property the list of link objects is sorted by
 * @property {Boolean}  sort_ascending  - true if list is sorted in ascending order, otherwise false
 *
 * @property {ResultRuleSummary}  rule_summary_result  - Rule results associated with this cache
 */
  
OpenAjax.a11y.cache.ImagesCache = function (dom_cache) {

  this.dom_cache = dom_cache;
  this.up_to_date = false;
 
  this.image_elements = [];
  this.length = 0;

  this.sort_property  = 'document_order';
  this.sort_ascending = true;

}; 

/**
 * @method addImageElement
 * 
 * @memberOf OpenAjax.a11y.cache.ImagesCache
 *
 * @desc Adds a image element to the list of image elements and generates a cache id for the object.
 *
 * @param  {ImageElement}  image_element  - image element object to add 
 *
 * @return {Number} Returns the length of the list of image element objects
 */

OpenAjax.a11y.cache.ImagesCache.prototype.addImageElement = function (image_element) {

  // item must exist and have the position property
  if (image_element) {
    this.length = this.length + 1;
    image_element.cache_id = "image_" + this.length; 
    image_element.document_order = this.length;
    this.image_elements.push(image_element);
  } 

  return this.length;

};

/**
 * @deprecated getImageElementByCacheId
 * 
 * @memberOf OpenAjax.a11y.cache.ImagesCache
 *
 * @desc Finds the the image element object with the matching cache id
 *
 * @param  {String}  cache_id  - Cache id of image element object
 *
 * @return {ImageElement | null} Returns cache image element object if cache id is found, otherwise null
 */

OpenAjax.a11y.cache.ImagesCache.prototype.getImageElementByCacheId = function (cache_id) {
  return this.getItemByCacheId(cache_id);
};

/**
 * @method getItemByCacheId
 * 
 * @memberOf OpenAjax.a11y.cache.ImagesCache
 *
 * @desc Finds the the image element object with the matching cache id
 *
 * @param  {String}  cache_id  - Cache id of image element object
 *
 * @return {ImageElement | null} Returns cache image element object if cache id is found, otherwise null
 */

OpenAjax.a11y.cache.ImagesCache.prototype.getItemByCacheId = function (cache_id) {

  var i;
  var image_elements_len = this.image_elements.length;

  if (cache_id && cache_id.length) {  
    for (i=0; i < image_elements_len; i++) {
      if (this.image_elements[i].cache_id == cache_id) {
        return this.image_elements[i];
      }
    } // end loop
  } 

 return null;
};


/**
 * @method emptyCache
 *
 * @memberOf OpenAjax.a11y.cache.ImagesCache
 *
 * @desc Resests the ImagesCache object properties and empties all the lists and arrays 
 */

OpenAjax.a11y.cache.ImagesCache.prototype.emptyCache = function () {

  this.image_elements = [];
  this.sort_property = 'document_order';
  this.up_to_date = false;

};

/**
 * @method updateCacheItems
 *
 * @memberOf OpenAjax.a11y.cache.ImagesCache
 *
 * @desc Updates the images cache object by checking to see if a dom element
 *          should be added to the cache
 *  
 * @param  {DOMElement}   dom_element   - dom element object to check for inclusion in images cache
 */
 
OpenAjax.a11y.cache.ImagesCache.prototype.updateCacheItems = function (dom_element) {

  if ((dom_element.tag_name === 'img') ||
      (dom_element.tag_name === 'area') ||
      ((typeof dom_element.role === 'string') && (dom_element.role === 'img'))) {

    var image_element = new OpenAjax.a11y.cache.ImageElement(dom_element, this.dom_cache.base_url);

    this.getNameForImage(image_element);
    
    this.addImageElement(image_element);
  
  }
  
};

/**
 * @method traverseDOMElementsForImageElements
 *
 * @memberOf OpenAjax.a11y.cache.ImagesCache
 *
 * @desc Traverses DOMElement objects in the tree to update the images cache 
 *
 * @param  {DOMElement}  dom_element - dom element object to check for inclusion in images cache
 */
 
OpenAjax.a11y.cache.ImagesCache.prototype.traverseDOMElementsForImageElements = function (dom_element) {

  if (!dom_element) return;

  if (dom_element.type == Node.ELEMENT_NODE) {

    this.updateCacheItems(dom_element);
  
    for (var i = 0; i < dom_element.child_dom_elements.length; i++ ) {
      this.traverseDOMElementsForImageElements(dom_element.child_dom_elements[i]);
    } // end loop
  }  
  
}; 

/**
 * @method updateCache
 *
 * @memberOf OpenAjax.a11y.cache.ImagesCache
 *
 * @desc Traverses the DOMElements to update the images cache
 *       NOTE: This function is only used when the specialized caches
 *       are build as rules need them.  In this condition, if the rules 
 *       dependent on the links cache are disabled, this cache would 
 *       not be updated
 */
 
OpenAjax.a11y.cache.ImagesCache.prototype.updateCache = function () {
  var i;
  var children = this.dom_cache.element_cache.child_dom_elements;
  var children_len = children.length;
 
  for (i=0; i < children_len; i++) {
    this.traverseDOMElementsForImageElements(children[i]);
  }  

  this.up_to_date = true;
};

/**
 * @method getNameFromARIALabel
 *
 * @memberOf OpenAjax.a11y.cache.DOMCache
 *
 * @desc Calculates a computed accessible name based on ALT attribute and ARIA label properties
 *
 * @param {Object} image - Image cache element object
 */

OpenAjax.a11y.cache.ImagesCache.prototype.getNameForImage = function (image) {

  var SOURCE = OpenAjax.a11y.SOURCE;

  var accessible_name = "";
  var accessible_name_source = SOURCE.NONE;
  var de = image.dom_element;
  
  if (de.aria_labelledby && de.aria_labelledby.length) {
    accessible_name = this.dom_cache.element_with_id_cache.getTextFromIds(de.aria_labelledby);
    accessible_name_source = SOURCE.ARIA_LABELLEDBY;
  }
  else if (de.aria_label && de.aria_label.length) {
    accessible_name = de.aria_label;
    accessible_name_source = SOURCE.ARIA_LABEL;
  }
  else if (de.has_alt) {
    accessible_name = de.alt;
    accessible_name_source = SOURCE.ALT_ATTRIBUTE;
  } 
  else if (de.title) {
    accessible_name = de.title;
    accessible_name_source = SOURCE.TITLE_ATTRIBUTE;
  }

  image.accessible_name = accessible_name;
  image.accessible_name_length = accessible_name.length;
  image.accessible_name_source = accessible_name_source;
  image.accessible_name_for_comparison = accessible_name.normalizeSpace().toLowerCase();

};

/* ---------------------------------------------------------------- */
/*                            ImageElement                          */
/* ---------------------------------------------------------------- */

/**
 * @constructor ImageElement
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates image element object representing information related to an image or area element on a web page
 *
 * @param  {DOMelement}   dom_element   - The dom element object representing the image or area element 
 *
 * @property  {DOMElement}  dom_element     - Reference to the dom element representing the image or area element
 * @property  {String}      cache_id        - String that uniquely identifies the cache element object in the cache
 * @property  {Number}      document_order  - Ordinal position of the image or area element in the document in relationship to other image or area elements
 *
 * @property  {Boolean}     is_image        - True if the role of the image is an image, otherwise false (i.e. through use of role attribute) 
 * @property  {Boolean}     is_presemtation - True if the role of the image has been changed to presentation, otherwise false  
 *
 * @property  {String}   source             - The url in the src property of an image element or href property of an area element 
 * @property  {Boolean}  src_is_a_file_name - The filename is an image file and not a data base or other programatic reference
 * @property  {String}   file_name          - The filename of the image
 *
 * @property  {String}   longdesc           - The url in the longdesc property of an image element  
 * @property  {Boolean}  has_longdesc       - Does the image have a longdesc attribute  
 * @property  {Number}   longdesc_is_broken - Constant representing if the url is broken or untested  
 * @property  {String}   longdesc_url       - The full URL of the longdesc attribute  
 *
 * @property  {String}   alt                   - Calculated accessible name of the link 
 * @property  {String}   alt_for_comparison   - Accessible name for comparison (i.e. lowercase, trimmed and space normalized)
 * @property  {Number}   alt_length           - Number of images that are descendents of the link
 *  
 * @property  {Number}   height  - Height of the image in pixels
 * @property  {Number}   width   - Width of the image in pixels
 */
 
OpenAjax.a11y.cache.ImageElement = function (dom_element, base_url) {

  var alt_value;

  if (!dom_element) return null;  

  var node = dom_element.node;
 
  this.dom_element    = dom_element;
  this.cache_id       = "";
  this.document_order = 0;
  
  this.source    = "";
  this.href      = "";
  this.file_name = "";
  this.is_image = true;
  this.is_presentation = false;
  
  if (dom_element.has_role && dom_element.role != 'img') this.is_image = false;
  if (dom_element.has_role && dom_element.role === 'presentation') this.is_presentation = true;

//  OpenAjax.a11y.logger.debug("Image element: " + dom_element.toString() + " has: " + dom_element.has_role + " role: " + dom_element.role  + " image: " + this.is_image + " presentation: " + this.is_presentation);

  if (dom_element.tag_name == 'img') {
  
    if (node.src) this.source = node.src;
    
    var pos = this.source.lastIndexOf('/');    
    
    var file_name = "";
    this.src_is_a_file_name = false;
    
    if (this.source.length && pos >= 0 ) {
      file_name = this.source.substring((pos+1)).toLowerCase();
      
      if ((file_name.indexOf('.png') >= 0) ||
          (file_name.indexOf('.jpg') >= 0) ||
          (file_name.indexOf('.jpeg') >= 0) ||
          (file_name.indexOf('.gif') >= 0)) this.src_is_a_file_name = true;
    }  
  
    this.file_name = file_name;
  }
  
  if (dom_element.tag_name === 'area') {
    this.href  = node.href;
  }

  this.accessible_name = null;
  this.accessible_name_length = null;
  this.accessible_name_source = OpenAjax.a11y.SOURCE.NONE;
  this.accessible_name_for_comparison = null;

  this.longdesc = node.getAttribute('longdesc');
  
  if (this.longdesc) {

    this.longdesc_url = this.longdesc;

    if (this.longdesc.indexOf('http:') == -1 ) {
      this.longdesc_url = base_url + this.longdesc;
    }

    this.has_longdesc = true;
    this.longdesc_is_broken = OpenAjax.a11y.util.urlExists(this.longdesc_url);
  }
  else {
    this.has_longdesc = false;
    this.longdesc     = null;
    this.longdesc_is_broken = null;
  }


  this.height   = node.offsetHeight;
  this.width    = node.offsetWidth;

  return this;
};

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.ImageElement
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.ImageElement.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.ImageElement
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style display objects
 */

OpenAjax.a11y.cache.ImageElement.prototype.getStyle = function () {

  return  this.dom_element.getStyle();
  
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.ImageElement
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of attribute display object
 */

OpenAjax.a11y.cache.ImageElement.prototype.getAttributes = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  var attributes = this.dom_element.getAttributes(unsorted);
     
  cache_nls.addPropertyIfDefined(attributes, this, 'title');
  cache_nls.addPropertyIfDefined(attributes, this, 'alt');
  cache_nls.addPropertyIfDefined(attributes, this, 'aria-label');
  cache_nls.addPropertyIfDefined(attributes, this, 'aria-labelledby');
  cache_nls.addPropertyIfDefined(attributes, this, 'longdesc');
  
  return attributes;
  
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.ImageElement
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of cache property display object
 */

OpenAjax.a11y.cache.ImageElement.prototype.getCacheProperties = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  var properties = [];
  
  cache_nls.addPropertyIfDefined(properties, this, 'accessible_name');
  cache_nls.addPropertyIfDefined(properties, this, 'accessible_name_source');
  cache_nls.addPropertyIfDefined(properties, this, 'height');
  cache_nls.addPropertyIfDefined(properties, this, 'width');
  cache_nls.addPropertyIfDefined(properties, this, 'document_order');
  cache_nls.addPropertyIfDefined(properties, this, 'has_longdesc');
  cache_nls.addPropertyIfDefined(properties, this, 'longdesc_url');
  cache_nls.addPropertyIfDefined(properties, this, 'longdesc_is_broken');
  
  return properties;
  
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.ImageElement
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.ImageElement.prototype.getCachePropertyValue = function (property) {

//  OpenAjax.a11y.logger.debug("Image property: " + property + " value= " + this[property]);

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};  

/**

 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.ImageElement
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event item display objects
 */

OpenAjax.a11y.cache.ImageElement.prototype.getEvents = function () {
   
  return this.dom_element.getEvents();
  
};


/**
 * @method getAltTextNLS
 *
 * @memberOf OpenAjax.a11y.cache.ImageElement
 *
 * @desc Returns an object with an NLS localized string and style properties
 *       If alt attribute is empty a empty alt text message will the returned 
 *
 * @return {String | Object} Returns a String if the alt attribute has content, 
 *                            but if label is empty it returns an object 
 *                            with a 'label and 'style' property
 */

OpenAjax.a11y.cache.ImageElement.prototype.getAltTextNLS = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  var alt_style = {};
  
  if (this.dom_element.has_alt) {
    if (this.alt_length) {
      return this.alt;
    }
    else {
      return cache_nls.getNLSEmptyAltTextMessage();
    }
  }
  else {
    return cache_nls.getNLSMissingAltMessage();
  }
  
};

/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.ImageElement
 *
 * @desc Creates a text string representation of the image element object 
 *
 * @return {String} Returns a text string representation of the image element object
 */
 
 OpenAjax.a11y.cache.ImageElement.prototype.toString = function () {
   var str = this.dom_element.tag_name;
   
   if (this.dom_element.computed_style.is_visible_onscreen == OpenAjax.a11y.VISIBILITY.HIDDEN) {
     str += " (hidden) : ";
   } 
   else {
     str += " (" + this.height + "x" + this.width + ") : ";
   }  
   
   if (this.src_is_a_file_name) {
     str += this.file_name;
   }
   else {
     str +=  "source is not a file name";   
   }
   
   return str;
};


/*
 * Copyright 2011, 2012 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


/* ---------------------------------------------------------------- */
/*                            LanguagesCache                        */
/* ---------------------------------------------------------------- */

/**
 * @constructor LanguagesCache
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Constructor for languages cache object which contains a list of 
 *    language items representing the language changes of content 
 *    in the in a document. The language items also contain a list of all the 
 *    dom element objects that share the same language
 *
 * @param {DOMCache}   dom_cache   - Reference to the DOMCache object 
 * 
 * @property {DOMCache} dom_cache  - Reference to the DOMCache object 
 * @property {Boolean}  up_to_date - Boolean true if the cache has been creating using the current DOMElements, else false
 *                                   NOTE: This is a common property of all caches and is used when selectively build caches 
 *                                         based on whether a rule needs the cache
 *
 * @property {String}    sort_property   - Property of language item that the list is sorted on  
 * @property {Boolean}   sort_ascending  - true if list is sorted by ascending values, otherwsie false 
 *
 * @property {Array}    language_items  - List of language items 
 * @property {Number}   length          - Number of language items in list 
 *
 * @property {ResultRuleSummary}  rule_summary_result  - Rule results associated with this cache
 */
OpenAjax.a11y.cache.LanguagesCache = function (dom_cache) {
    
  this.dom_cache  = dom_cache;
  this.up_to_date = false;
    
  this.dom_elements =[];
        
};

/**
 * @method addLanguageItem
 *
 * @memberOf OpenAjax.a11y.cache.LanguagesCache
 *
 * @desc Adds a DOM Element object with an language property to the langauge item list.
 *       If the abreviation item does not exist the function will create one
 *
 * @param {DOMElement}  dom_element  - dom element to add to a abbreviation list
 */

OpenAjax.a11y.cache.LanguagesCache.prototype.addLanguageItem = function (dom_element) {
 
   this.dom_elements.push(dom_element);
      
};


/**
 * @method emptyCache
 *
 * @memberOf OpenAjax.a11y.cache.LanguagesCache
 *
 * @desc Empties all the language items from the cache
 */

OpenAjax.a11y.cache.LanguagesCache.prototype.emptyCache = function () {
    
    this.dom_elements.length = 0;
    this.up_to_date = false;
};

/**
 * @method updateCacheItems
 *
 * @memberOf OpenAjax.a11y.cache.LanguagesCache
 *
 * @desc Updates the language cache object with information from a dom element object
 *       This is used during the creation of the cache and is used by the functions for
 *       either creating the cache all at one time or selectively
 *
 * @param {DOMElement}  dom_element  - DOM Element object to add to the language cache
 */

OpenAjax.a11y.cache.LanguagesCache.prototype.updateCacheItems = function (dom_element) {
    
    if (dom_element.has_lang && dom_element.lang.length) {
      this.addLanguageItem(dom_element);
    }  
};

/**
 * @method traverseDOMElementsForLanguages
 *
 * @memberOf OpenAjax.a11y.cache.LanguagesCache
 *
 * @desc Traverses the DOMElements to update the language cache
 */

OpenAjax.a11y.cache.LanguagesCache.prototype.traverseDOMElementsForLanguages = function (dom_element) {
    
    var i;
    if (!dom_element) return;
    
    if (dom_element.type == Node.ELEMENT_NODE) {
        
        this.updateCacheItems(dom_element);
        
        for (i = 0; i < dom_element.child_dom_elements.length; i++) {
            this.traverseDOMElementsForLanguages(dom_element.child_dom_elements[i]);
        }
        // end loop
    }
};

/**
 * @method updateCache
 *
 * @memberOf OpenAjax.a11y.cache.LanguagesCache
 *
 * @desc Traverses the DOMElements to update the language cache
 *    This function is used to update the language cache 
 *    when needed by a rule, it sets the up to date flag when done
 */

OpenAjax.a11y.cache.LanguagesCache.prototype.updateCache = function () {
    var i;
    var children = this.dom_cache.element_cache.child_dom_elements;
    var children_len = children.length;
    
    for (i = 0; i < children_len; i++) {
        this.traverseDOMElementsForLanguages(children[i]);
    }
    
    this.up_to_date = true;
};


/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.LanguagesCache
 *
 * @desc Returns a text string representation of the language cache object 
 *
 * @return {String} Returns string represention the language cache object
 */

OpenAjax.a11y.cache.LanguagesCache.prototype.toString = function () {
    
    var i;
    
    var str = "\n\n Elements with Lang Attributes\n";
    
    var dom_elements     = this.dom_elements;
    var dom_elements_len = dom_elements.length;
    
    for (i = 0; i < dom_elements_len; i++) {
        str += dom_elements[i].toString();
    }
    // end loop
    
    return str;
};

/*
 * Copyright 2011-2013 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* ---------------------------------------------------------------- */
/*                            LinkCache                             */
/* ---------------------------------------------------------------- */

/**
 * @constructor LinksCache
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates cache object representing information related to links in a web page
 *
 * @param {DOMCache}   dom_cache   - Reference to the DOMCache object 
 * 
 * @property {DOMCache} dom_cache  - Reference to the DOMCache object       
 * @property {Boolean}  up_to_date - Boolean true if the cache has been creating using the current DOMElements, else false
 *                                   NOTE: This is a common property of all caches and is used when selectively build caches 
 *                                         based on whether a rule needs the cache
 *
 * @property {Array}    area_elements  - List of area element objects in the document 
 * @property {Array}    link_elements  - List of link element objects in the document 
 * @property {Number}   length         - Number of link element objects in the list 
 *
 * @property {String}   this.sort_property   - Link element object property the list of link objects is sorted by
 * @property {Boolean}  this.sort_ascending  - true if list is sorted in ascending order, otherwise false
 *
 * @property {Array}    links_sorted_by_href  - List of link element object sorted by href values;
 * @property {Array}    links_sorted_by_name  - List of link element object sorted by there accessible name (i.e link text);
 *  
 * @property {Boolean}  sorted_by_href_ready  - True if list of link element objects sorted by href values is ready for use in rules
 * @property {Boolean}  sorted_by_name_ready  - True if list of link element objects sorted by name values is ready for use in rules
 */

OpenAjax.a11y.cache.LinksCache = function (dom_cache) {

  this.dom_cache = dom_cache;
  this.up_to_date = false;
  
  this.area_elements = [];
  this.link_elements = [];
  this.length = 0;

  this.links_sorted_by_href = [];
  this.links_sorted_by_name = [];
  
  this.sorted_by_name_ready = false;
  this.sorted_by_href_ready = false;
  
}; 

/**
 * @method getLinkElementsSortedByName
 * 
 * @memberOf OpenAjax.a11y.cache.LinksCache
 *
 * @desc Returns a list of link elements sorted by accessible name property
 *
 * @return {Array}  Returns an array of LinkElement objects
 */

OpenAjax.a11y.cache.LinksCache.prototype.getLinkElementsSortedByName = function () {

  function compare(a,b) {
  
    return ((a.accessible_name_for_comparison > b.accessible_name_for_comparison) ||
            ((a.accessible_name_for_comparison === b.accessible_name_for_comparison) &&
             (a.href > b.href)));
  
  }
  
 
  if (!this.sorted_by_name_ready) {
  
    this.links_sorted_by_name.sort(compare);
        
    this.sorted_by_name_ready = true;
    
  }

/*
  OpenAjax.a11y.logger.debug( "Number of Links: " + this.links_sorted_by_name.length);

  for (i = 0; i < this.links_sorted_by_name.length; i++ ) {
   
    var l = this.links_sorted_by_name[i];
  
    OpenAjax.a11y.logger.debug( i + "  Name: " + l.accessible_name + "  Compare Name: " + l.accessible_name_for_comparison + " HREF: " + l.href );
  
  }
*/
  return this.links_sorted_by_name;

};

/**
 * @method getLinksThatShareTheSameName
 * 
 * @memberOf OpenAjax.a11y.cache.LinksCache
 *
 * @desc Returns a list of links that share the same accessible name
 *       links that are hidden are ignored
 *
 * @return  {Array} Returns at array of same name objects with the following properties:<br/> 
 *                  links: an array of link objects<br/>
 *                  same_names: True if all the links in the array share the same accessible name<br/>
 *                  names_count: Number of different accessible names in the links array<br/>
 */

OpenAjax.a11y.cache.LinksCache.prototype.getLinksThatShareTheSameName = function () {

  function checkForUniqueDescriptions(sns) {
  
    function compareDescriptions( a, b ) {
      return a.accessible_description_for_comparison < b.accessible_description_for_comparison;
    }
  
    var same_name = sns.links.sort(compareDescriptions);
    sns.unique_descriptions = true;
        
    for (var i = 1; i < same_name.length; i++) {
      var name1 = same_name[i-1];
      var name2 = same_name[i];
          
      sns.unique_descriptions = same_names.unique_descriptions && 
      ((name1.accessible_description_for_comparison !== name2.accessible_description_for_comparison) ||
       (name1.href === name2.href));
    }
    
  }



  var VISIBILITY  = OpenAjax.a11y.VISIBILITY;
  
  var list_of_same_names = [];
  var same_names = null;
  
  var names = this.getLinkElementsSortedByName();
  var names_len = names.length;
  
  var i = 0;
  var j = 1;
  
  while (j < names_len) {
  
    var name1 = names[i];
    var name2 = names[j];
  
    if (name1.dom_element.computed_style.is_visible_to_at === VISIBILITY.HIDDEN) {
      i++;
      j++;
      continue;  
    }
    
    if (name2.dom_element.computed_style.is_visible_to_at === VISIBILITY.HIDDEN) {
      j++;
      continue;
    }  
  
    if (name1.accessible_name_for_comparison === name2.accessible_name_for_comparison) {
       
      if (same_names) {
       
        same_names.links.push(name2);
        
        if (name1.href !== name2.href) {
            same_names.same_hrefs = false;
            same_names.hrefs_count += 1;
        }  

      }
      else {
        same_names = {
          links       : [name1, name2],
          same_hrefs  : name1.href === name2.href,
          hrefs_count : (name1.href === name2.href) ? 1 : 2
        };  
      }
    }
    else {
      if (same_names) {
        checkForUniqueDescriptions(same_names);
        list_of_same_names.push(same_names);
        same_names = null;
      }
    }
    
   i = j; 
   j++; 
  }

  if (same_names) {
    checkForUniqueDescriptions(same_names);
    list_of_same_names.push(same_names);
  }  

/*
  OpenAjax.a11y.logger.debug( "Number of same name objects in list: " + list_of_same_names.length);

  for (i = 0; i < list_of_same_names.length; i++ ) {
    var item = list_of_same_names[i]; 
    OpenAjax.a11y.logger.debug( i  + " NAME: " + item.links[0].accessible_name + "  Number: " + item.links.length + "  Same HREF: " + item.same_hrefs);  
  }
*/
  return list_of_same_names;
  
};

/**
 * @method getLinkElementsSortedByHREF
 * 
 * @memberOf OpenAjax.a11y.cache.LinksCache
 *
 * @desc Returns a list of link elements sorted by accessible name property
 * 
 * @return {Array}  Returns an array of LinkElement objects
 */

OpenAjax.a11y.cache.LinksCache.prototype.getLinkElementsSortedByHREF = function () {

  function compare(a,b) {
  
    return ((a.href > b.href) ||
            ((a.href === b.href) &&
             (a.accessible_name_for_comparison > b.accessible_name_for_comparison)));
  
  }
  
 
  if (!this.sorted_by_href_ready) {
    this.links_sorted_by_href.sort(compare);
    this.sorted_by_href_ready = true;
    
    
  }

/*
  OpenAjax.a11y.logger.debug( "Number of Links: " + this.links_sorted_by_href.length);

  for (i = 0; i < this.links_sorted_by_href.length; i++ ) {
    var l = this.links_sorted_by_href[i]; 
    OpenAjax.a11y.logger.debug( i  + " HREF: " + l.href + "  Name: " + l.accessible_name + "  Compare Name: " + l.accessible_name_for_comparison );  
  }
*/
  return this.links_sorted_by_href;
  
};


/**
 * @method getLinksThatShareTheSameHREF
 * 
 * @memberOf OpenAjax.a11y.cache.LinksCache
 *
 * @desc Returns a list of link elements that share the same HREF value
 *       links that are hidden are ignored
 *
 * @return  {Array} Returns at array of same href objects, with the followin properties:<br/>
 *                  links: an array of link objects<br/>
 *                  same_names: True if all the links in the array share the same accessible name<br/>
 *                  names_count: Number of different accessible names in the links array<br/>
 */

OpenAjax.a11y.cache.LinksCache.prototype.getLinksThatShareTheSameHREF = function () {

  var VISIBILITY  = OpenAjax.a11y.VISIBILITY;
  
  var list_of_same_hrefs = [];
  var same_hrefs = null;
  
  var hrefs = this.getLinkElementsSortedByHREF();
  var hrefs_len = hrefs.length;
  
  var i = 0;
  var j = 1;
  
  while (j < hrefs_len) {
  
    var href1 = hrefs[i];
    var href2 = hrefs[j];
  
    if (href1.dom_element.computed_style.is_visible_to_at === VISIBILITY.HIDDEN) {
      i++;
      j++;
      continue;  
    }
    
    if (href2.dom_element.computed_style.is_visible_to_at === VISIBILITY.HIDDEN) {
      j++;
      continue;
    }  
  
    if (href1.href ===  href2.href) {
       
      if (same_hrefs) {
       
        same_hrefs.links.push(href2);
        
        if (href1.accessible_name_for_comparison !== href2.accessible_name_for_comparison) {
          same_hrefs.same_names = false;
          same_hrefs.names_count += 1;
        }
        
      }
      else {
        same_hrefs = {
          links : [href1, href2],
          same_names : href1.accessible_name_for_comparison === href2.accessible_name_for_comparison,
          names_count : (href1.accessible_name_for_comparison === href2.accessible_name_for_comparison) ? 1 : 2
        };  
      }
    }
    else {
      if (same_hrefs) {
        list_of_same_hrefs.push(same_hrefs);
        same_hrefs = null;
      }
    }
    
   i = j; 
   j++; 
  }

  if (same_hrefs) list_of_same_hrefs.push(same_hrefs);
/*
  OpenAjax.a11y.logger.debug( "Number of DUP HREF objects: " + list_of_same_hrefs.length);

  for (i = 0; i < list_of_same_hrefs.length; i++ ) {
    var item = list_of_same_hrefs[i]; 
    OpenAjax.a11y.logger.debug( i  + " HREF: " + item.links[0].href + "  Number: " + item.links.length + "  Same Name: " + item.same_names);  
  }
*/
  return list_of_same_hrefs;
  
};


/**
 * @method addLinkElement
 * 
 * @memberOf OpenAjax.a11y.cache.LinksCache
 *
 * @desc Adds a link element to the list of link elements and generates a cache id for the object.
 *       Checks if the link has a duplicate href or name in the document 
 *
 * @param  {LinkElement}  link_element  - link element to add 
 *
 * @return {Number} Returns the length of the list of link elements
 */

OpenAjax.a11y.cache.LinksCache.prototype.addLinkElement = function (link_element) {

  // item must exist and have the position property
  if (link_element) {
    this.links_sorted_by_name.push(link_element);
    this.links_sorted_by_href.push(link_element);

    this.length = this.length + 1;
    link_element.cache_id = "link_" + this.length; 
    link_element.document_order = this.length;
    this.link_elements.push(link_element);
    
    if (link_element.dom_element.tag_name === 'area') {
      this.area_elements.push(link_element);
    }
  } 
  
  return this.length;
};

/**
 * @deprecated getLinkElementByCacheId
 * 
 * @memberOf OpenAjax.a11y.cache.LinksCache
 *
 * @desc Finds the the link element object with the matching cache id
 *
 * @param  {String }  cache_id  - Cache id of link element object
 *
 * @return {LinkElement} Returns cache link element object if cache id is found, otherwise null
 */

OpenAjax.a11y.cache.LinksCache.prototype.getLinkElementByCacheId = function (cache_id) {
  return this.getItemByCacheId(cache_id);
};

/**
 * @method getItemByCacheId
 * 
 * @memberOf OpenAjax.a11y.cache.LinksCache
 *
 * @desc Finds the the link element object with the matching cache id
 *
 * @param  {String }  cache_id  - Cache id of link element object
 *
 * @return {LinkElement} Returns cache link element object if cache id is found, otherwise null
 */

OpenAjax.a11y.cache.LinksCache.prototype.getItemByCacheId = function (cache_id) {

  var i;

  var link_elements_len = this.link_elements.length;

  if (cache_id && cache_id.length) {  
   for (i=0; i < link_elements_len; i++) {
     if (this.link_elements[i].cache_id == cache_id) {
       return this.link_elements[i];
     }
   } // end loop
 } 

 return null;
};

/**
 * @method emptyCache
 *
 * @memberOf OpenAjax.a11y.cache.LinksCache
 *
 * @desc Resests the LinksCache object properties and empties all the lists and arrays 
 */
 
OpenAjax.a11y.cache.LinksCache.prototype.emptyCache = function () {

  this.link_elements = [];
  this.length = 0;
  this.sort_property = 'document_order';
  this.up_to_date = false;

};

/**
 * @method updateCacheItems
 *
 * @memberOf OpenAjax.a11y.cache.LinksCache
 *
 * @desc Updates the links cache object by checking to see if a dom element
 *          should be added to the cache
 *  
 * @param  {DOMElement}   dom_element   - DOMElement object to check for inclusion in links cache
 */
 
OpenAjax.a11y.cache.LinksCache.prototype.updateCacheItems = function (dom_element) {

  var link_element;

  if ((dom_element.tag_name === 'a'    && (dom_element.has_href || dom_element.hasClickEvents()) && !dom_element.is_widget) ||
      (dom_element.tag_name === 'area' && (dom_element.has_href || dom_element.hasClickEvents()) && !dom_element.is_widget) ||
      ((typeof dom_element.role === 'string') && (dom_element.role === 'link'))) {
      
        dom_element.is_interactive = true;
      
        link_element = new OpenAjax.a11y.cache.LinkElement(dom_element);    
    
        this.dom_cache.getNameForLink(link_element);
    
        this.dom_cache.links_cache.addLinkElement(link_element);
  }
   
};

/**
 * @method traverseDOMElementsForLinkElements
 *
 * @memberOf OpenAjax.a11y.cache.LinksCache
 *
 * @desc Traverses dom element objects in the tree to update the links cache 
 *
 * @param  {DOMElement}  dom_element - dom element object to check for inclusion in links cache
 */
 
OpenAjax.a11y.cache.LinksCache.prototype.traverseDOMElementsForLinkElements = function (dom_element) {
 
  var i;

  if (!dom_element) return;

  if (dom_element.type == Node.ELEMENT_NODE) {

    this.updateCacheItems(dom_element);
  
    for (i = 0; i < dom_element.child_dom_elements.length; i++) {
      this.traverseDOMElementsForLinkElements(dom_element.child_dom_elements[i]);
    } // end loop
  }  
  
}; 


/**
 * @method updateCache
 *
 * @memberOf OpenAjax.a11y.cache.LinksCache
 *
 * @desc Traverses the DOMElements to update the links cache
 *       NOTE: This function is only used when the specialized caches
 *       are build as rules need them.  In this condition, if the rules 
 *       dependent on the links cache are disabled, this cache would 
 *       not be updated
 */
 
OpenAjax.a11y.cache.LinksCache.prototype.updateCache = function () {

  var i;
  var children = this.dom_cache.element_cache.child_dom_elements;
  var children_len = children.length;
 
  for (i=0; i < children_len; i++) {
    this.traverseDOMElementsForLinkElements(children[i]);
  }  

  this.up_to_date = true;
 
};


/* ---------------------------------------------------------------- */
/*                            LinkElement                           */
/* ---------------------------------------------------------------- */

/**
 * @constructor LinkElement
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates link element object representing information related to an a or area element on a web page
 *
 * @param  {DOMElement}   dom_element   - The dom element object representing the a or area element 
 *
 * @property  {DOMElement}  dom_element     - Reference to the dom element representing the a or area element
 * @property  {String}      cache_id        - String that uniquely identifies the cache element object in the cache
 * @property  {Number}      document_order  - Ordinal position of the a or area element in the document in relationship to other a or area elements
 *
 * @property  {String}   href    - The absolute path of the href value 
 * @property  {Boolean}  is_url  - true if href is a url, otherwise false (i.e. internal link or broken)
 * @property  {Boolean}  is_link - true if href is a internal or exteranl link, otherwise
 * @property  {Number}   link_type - Type of link contstant
 *
 * @property  {String}   tab_index  - value of the tabindex attribute
 * @property  {String}   name_attr  - value of the name attribute
 * @property  {String}   target     - value of the target attribute
 *
 * @property  {String}      accessible_name                - The accessible name for the link
 * @property  {String}      accessible_name_for_comparison - The accessible name for the link used for comparison
 * @property  {Number}      accessible_name_length         - Length of the accessible name used for comparison
 * @property  {Number}      accessible_name_source         - Numeric constant representing the source of the accessible name
 *                                                            - aria-labelledby
 *                                                            - aria-label
 *                                                            - child text content
 *                                                            - title
 *
 * @property  {String}      accessible_description                 - Accessible description for the link (from aria-describedby)
 * @property  {String}      accessible_description_for_comparison  - Accessible description for the link used for comparison 
 *  
 * @property  {Number}   height  - Height of the link in pixels
 * @property  {Number}   width   - Width of the link in pixels
 */
 
OpenAjax.a11y.cache.LinkElement = function (dom_element) {

  function getTypeOfLink(href, name, id) {
  
    href = href.toLowerCase();
    
    if (typeof href != 'string') return OpenAjax.a11y.LINK_TYPE.OTHER;
   
    if (href.length === 0) { 
      if ((name && name.length) || (id && id.length)) 
        return OpenAjax.a11y.LINK_TYPE.TARGET;
      else  
        return OpenAjax.a11y.LINK_TYPE.EMPTY;
    }    

    if (href === '#') return OpenAjax.a11y.LINK_TYPE.EMPTY;

    if (href.indexOf('http://') >= 0) return OpenAjax.a11y.LINK_TYPE.HTTP;
    else
      if (href.indexOf('https://') >= 0) return OpenAjax.a11y.LINK_TYPE.HTTPS;
      else
        if (href.indexOf('ftp://') >= 0) return OpenAjax.a11y.LINK_TYPE.FTP;
        else
          if (href.indexOf('ftps://') >= 0) return OpenAjax.a11y.LINK_TYPE.FTPS;
          else 
            if (href.indexOf('file://') >= 0) return OpenAjax.a11y.LINK_TYPE.FILE;
            else 
              if (href.indexOf('javascript:') >= 0) return OpenAjax.a11y.LINK_TYPE.JAVASCRIPT;
              else 
                if (href.indexOf('mailto:') >= 0) return OpenAjax.a11y.LINK_TYPE.MAILTO;
                else 
                  if (href[0] === '#') return OpenAjax.a11y.LINK_TYPE.INTERNAL;
 
    return OpenAjax.a11y.LINK_TYPE.HTTP;
  }


  function testIfHrefIsURL(url) {
  
    if (typeof href != 'string') return false;
  
    if (url.indexOf('http://') >= 0) return true;
    else
      if (url.indexOf('https://') >= 0) return true;
      else
        if (url.indexOf('ftp://') >= 0) return true;
        else
          if (url.indexOf('ftps://') >= 0) return true;
          else 
            if (url.indexOf('file://') >= 0) return true;
 
    return false;
  }

  if (!dom_element.node) return;

  var href = dom_element.node.href;

  if ((typeof dom_element.role === 'string') &&
      (dom_element.role === 'link') && 
      (href !== 'string')) href = "javascript:onclick";

  this.dom_element    = dom_element;
  this.cache_id       = "";
  this.document_order = 0;
  
  this.role = dom_element.role;
 
  this.href  = href;
  this.is_url = testIfHrefIsURL(href);
  
  if (this.is_url) { 
    this.is_broken = OpenAjax.a11y.util.urlExists(href);
  }
  else {
    this.is_broken = OpenAjax.a11y.URL_RESULT.NOT_A_URL;
  }

  this.tab_index = dom_element.node.tabIndex;
  
  this.name_attribute = dom_element.node.getAttribute("name");
  this.is_target = this.name_attribute && (this.name_attribute.length > 0);

  var link_type = getTypeOfLink(href, this.name_attribute, dom_element.id);
  
  this.link_type = link_type;
  
  this.is_link = false;

  if ((link_type !== OpenAjax.a11y.LINK_TYPE.OTHER) &&
      (link_type !== OpenAjax.a11y.LINK_TYPE.TARGET) &&
      (link_type !== OpenAjax.a11y.LINK_TYPE.EMPTY)) this.is_link = true;
  
  this.target  = dom_element.node.getAttribute("target");

  var ano = dom_element.getTextObject();
  
  var cs = dom_element.computed_style;
   
  // If the link is an image, use the image height and width
  if (ano.height > cs.height) cs.height = ano.height;
  if (ano.width > cs.width) cs.width = ano.width;

};

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.LinkElement
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.LinkElement.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.LinkElement
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style display objects
 */

OpenAjax.a11y.cache.LinkElement.prototype.getStyle = function () {

  return this.dom_element.getStyle();
  
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.LinkElement
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of attribute display object
 */

OpenAjax.a11y.cache.LinkElement.prototype.getAttributes = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  var attributes = this.dom_element.getAttributes();
  
  cache_nls.addPropertyIfDefined(attributes, this, 'href');

  cache_nls.addPropertyIfDefined(attributes, this, 'tab_index');
  cache_nls.addPropertyIfDefined(attributes, this, 'name_attribute');
  cache_nls.addPropertyIfDefined(attributes, this, 'target');
  cache_nls.addPropertyIfDefined(attributes, this, 'aria-describedby');
  cache_nls.addPropertyIfDefined(attributes, this, 'aria-labelledby');
  cache_nls.addPropertyIfDefined(attributes, this, 'aria-label');
  cache_nls.addPropertyIfDefined(attributes, this, 'title');
  
  if (!unsorted) this.dom_element.sortItems(attributes);
  
  return attributes;
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.LinkElement
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of cache property display object
 */

OpenAjax.a11y.cache.LinkElement.prototype.getCacheProperties = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;

  var properties = this.dom_element.getCacheProperties(unsorted);

  cache_nls.addPropertyIfDefined(properties, this, 'accessible_name');
  cache_nls.addPropertyIfDefined(properties, this, 'accessible_name_for_comparison');
  cache_nls.addPropertyIfDefined(properties, this, 'accessible_name_source');
  cache_nls.addPropertyIfDefined(properties, this, 'accessible_description');
  cache_nls.addPropertyIfDefined(properties, this, 'accessible_description_for_comparison');

  cache_nls.addPropertyIfDefined(properties, this, 'is_broken');
  cache_nls.addPropertyIfDefined(properties, this, 'is_url');
  cache_nls.addPropertyIfDefined(properties, this, 'is_target');
  cache_nls.addPropertyIfDefined(properties, this, 'link_type');

  if (!unsorted) this.dom_element.sortItems(properties);

  return properties;
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.LinkElement
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.LinkElement.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};

/**
 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.LinkElement
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event item display objects
 */

OpenAjax.a11y.cache.LinkElement.prototype.getEvents = function () {
   
  return this.dom_element.getEvents();
  
};

/**
 * @method getLinkType
 *
 * @memberOf OpenAjax.a11y.cache.LinkElement
 *
 * @desc Returns an array of style items 
 *
 * @return {String} Returns a NLS string representing the type of link
 */

OpenAjax.a11y.cache.LinkElement.prototype.getLinkType = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  return cache_nls.getValueNLS('link_type', this.link_type);
  
};

/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.LinkElement
 *
 * @desc Creates a text string representation of the link element object 
 *
 * @return {String} Returns a text string representation of the link element object
 */
 
 OpenAjax.a11y.cache.LinkElement.prototype.toString = function () {
  
   var str = "";
   
   if ((this.dom_element.tag_name === 'a') || (this.dom_element.tag_name === 'area')) {
     str = this.dom_element.tag_name + " : " + this.accessible_name; 
   }
   else {
     str = this.dom_element.tag_name + "[role='link']: " + this.accessible_name;    
   }
   return str;
 };


/*
 * Copyright 2011, 2012 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
 
/* ---------------------------------------------------------------- */
/*                              ListInfo                            */
/* ---------------------------------------------------------------- */

/**
 * @constructor ListInfo
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates a list information object for preserving the current list information 
 *        when traversing the DOM
 *
 * @param {ListInfo} list_info - Current list information object
 *
 * @property {ListElement | ContainerElement}  list_element      - Parent container list or container element object 
 * @property {ContainerElement}                container_element - Parent container element object 
 * @property {LandmarkElement}                 parent_landmark  - Parent landmark element object 
 */

OpenAjax.a11y.cache.ListInfo = function (list_info) {

  if (list_info) {
    this.list_element      = list_info.list_element;
    this.container_element = list_info.container_element;
    this.parent_landmark   = list_info.parent_landmark;
  }
  else {
    this.list_element      = null;
    this.container_element = null;
    this.parent_landmark   = null;
  }

};

/* ---------------------------------------------------------------- */
/*                            ListsCache                            */
/* ---------------------------------------------------------------- */

/**
 * @constructor ListsCache
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Constructor for lists cache object which contains a list of 
 *    list items representing the list (i.e ul, ol , dl, li, dt and dd) 
 *    elements defined in a document. 
 *
 * @param {DOMCache}   dom_cache   - Reference to the DOMCache object 
 * 
 * @property {DOMCache} dom_cache  - Reference to the DOMCache object 
 *         
 * @property {Boolean}  up_to_date - Boolean true if the cache has been creating using the current DOMElements, else false
 *                                   NOTE: This is a common property of all caches and is used when selectively build caches 
 *                                         based on whether a rule needs the cache
 *
 * @property {Array}    child_cache_elements  - Root array of the tree representation of the list elements in the document 
 *
 * @property {Array}   container_elements  - List of the container element objects in the document that are not children of a container item 
 * @property {Array}   listitem_elements   - List of the listitem element objects  
 * @property {Number}  length              - Number of containter element objects in list 
 *
 * @property {String}   sort_property   - Property of contanter element objectthe list is sorted on  
 * @property {Boolean}  sort_ascending  - true if list is sorted by ascending values, otherwise false 
 *
 * @property {Number}  landmark_count   - Number of containter element objects in list 
 *
 * @property {ResultRuleSummary}  rule_summary_result  - Rule results associated with this cache
 */

OpenAjax.a11y.cache.ListsCache = function (dom_cache) {

  this.dom_cache = dom_cache;
  this.up_to_date = false;
  
  this.child_cache_elements = []; 

  this.container_elements = [];  
  this.listitem_elements = [];  
  this.length = 0;

  this.sort_property  = 'document_order';
  this.sort_ascending = true;

  this.landmark_count  = 0;
  
};

/** 
 * @method addContainerElement
 *
 * @memberOf OpenAjax.a11y.cache.ListsCache
 *
 * @desc Adds a container element object to the list of container elements  
 *
 * @param  {ContainerElement} container_element   - Container element object to add 
 *
 * @return  {Number} Returns the number of container element objects in the list of container element objects
 */

OpenAjax.a11y.cache.ListsCache.prototype.addContainerElement = function (container_element) {

  if (container_element) {
    this.length += 1;
    container_element.document_order = this.length;
    container_element.cache_id = "con_" + this.length;
    this.container_elements.push(container_element);
    return true;
  }

  return false;

};

/**
 * @method updateCacheItems
 *
 * @memberOf OpenAjax.a11y.cache.ListsCache
 *
 * @desc Update the ListsCache by checking to see if the current
 *       DOMElement is a list-related element and that consequently
 *       a new list element object should be added to this cache.
 *
 * @param  {DOMElement}   dom_element  - dom element object to check for inclusion in lists cache
 * @param  {ListInfo}     list_info    - Information about the current list relationships in the DOM
 *
 * @return {ListInfo}  Returns updated list information object 
 */

OpenAjax.a11y.cache.ListsCache.prototype.updateCacheItems = function (dom_element, list_info) {

  var li = new OpenAjax.a11y.cache.ListInfo(list_info);

  // check whether we need to add a new ListElement
  switch (dom_element.tag_name) {
  
  case 'ul':
  case 'ol':
  case 'dl':
  
    var ce = new OpenAjax.a11y.cache.ContainerElement(dom_element, list_info);
    
    if (!list_info.container_element) this.addContainerElement(ce);

    if (list_info.list_element) {
      list_info.list_element.addChildElement(ce);
    }
    else {
      this.addChildElement(ce);
    }

    li.container_element = ce;
    li.list_element      = ce;
    break;

  case 'li':
  case 'dt':
  case 'dd':

    var le = new OpenAjax.a11y.cache.ListElement(dom_element, list_info.container_element);

    if (list_info.container_element) list_info.container_element.addListElement(le);

    if (list_info.list_element) {
      list_info.list_element.addChildElement(le);
    }
    else {
      this.addChildElement(le);
    }
    
    this.listitem_elements.push(le);
    
    li.list_element = le;

    break;

  default:
    break;
  
  } // end switch


  if ((dom_element.tag_name === 'a') ||
      (dom_element.getRole() === 'link')) {
  
    if (list_info.list_element &&
        list_info.list_element.link_count &&
        dom_element.node.href && 
        dom_element.node.href.length) {
      list_info.list_element.link_count += 1;
    }
  }  

  if ((typeof dom_element.role === 'string') &&
      ((dom_element.role == 'region')    ||
       (dom_element.role == 'main')     || 
       (dom_element.role == 'navigation')  ||
       (dom_element.role == 'search')    ||
       (dom_element.role == 'applicaton')  ||
       (dom_element.role == 'banner')    ||
       (dom_element.role == 'complementary') ||
       (dom_element.role == 'contentinfo')  ||
       (dom_element.role == 'form'))) {
   
    le = new OpenAjax.a11y.cache.LandmarkElement(dom_element, list_info.parent_landmark);   
    
    le.cache_id = "listLandmark_" + this.landmark_count;
    
    this.landmark_count += 1;

    this.dom_cache.getNameFromARIALabel(le);

    if (list_info.list_element) {
      list_info.list_element.addChildElement(le);
    }
    else {
      this.addChildElement(le);
    }
  
    li.parent_landmark = le;
    li.list_element    = le;
    
  }

  return li;

};

/**
 * @method traverseDOMElementsForListElements
 *
 * @memberOf OpenAjax.a11y.cache.ListsCache
 *
 * @desc Traverses the DOMElements to update the abbreviation cache
 */
 
OpenAjax.a11y.cache.ListsCache.prototype.traverseDOMElementsForListElements = function (dom_element, list_info) {
 
  var i;
  var li;

  if (!dom_element) return;

  if (dom_element.type == Node.ELEMENT_NODE) {

    li = this.updateCacheItems(dom_element, list_info);
  
    for (i = 0; i < dom_element.child_dom_elements.length; i++ ) {
      this.traverseDOMElementsForListElements(dom_element.child_dom_elements[i], li);
    } // end loop
  
  }  
  
}; 

/**
 * @method updateCache
 *
 * @memberOf OpenAjax.a11y.cache.ListsCache
 *
 * @desc Traverses the DOMElements to update the list cache
 *    This function is used to update the list cache 
 *    when needed by a rule, it sets the up to date flag when done
 */
 
OpenAjax.a11y.cache.ListsCache.prototype.updateCache = function () {

 var i;
 var children = this.dom_cache.element_cache.child_dom_elements;
 var children_len = children.length;
 
 var list_info = new OpenAjax.a11y.cache.ListInfo(null);
  
 for (i = 0; i < children_len; i++) {
  this.traverseDOMElementsForListElements(children[i], list_info);
 }  
 
 this.up_to_date = true;
};

/**
 * @method addChildElement
 *
 * @memberOf OpenAjax.a11y.cache.ListsCache
 *
 * @desc Add a top-level list element object to the lists cache
 *
 * @param {ContainerElement | ListElement | LandmarkElement } list_element - list cache element object to add to the list cache
 *
 * @return {boolean} indicating success or failure
 */

OpenAjax.a11y.cache.ListsCache.prototype.addChildElement = function (list_element) {

  if (list_element) {
    this.child_cache_elements.push(list_element);
    return true;
  }

  return false;

};

/**
 * @deprecated getListElementByCacheId
 *
 * @memberOf OpenAjax.a11y.cache.ListsCache
 *
 * @desc retrieve list element from lists cache based on its cache id
 *
 * @param  {String}  cache_id  -  cache id of the list cache element object to find
 *
 * @return {ListElement} Returns list cache object if cache id found, otherwise null 
 */

OpenAjax.a11y.cache.ListsCache.prototype.getListElementByCacheId = function (cache_id) {
 return this.getItemByCacheId(cache_id);
};

/**
 * @method getItemByCacheId
 *
 * @memberOf OpenAjax.a11y.cache.ListsCache
 *
 * @desc retrieve list element from lists cache based on its cache id
 *
 * @param  {String}  cache_id  -  cache id of the list cache element object to find
 *
 * @return {ListElement} Returns list cache object if cache id found, otherwise null 
 */

OpenAjax.a11y.cache.ListsCache.prototype.getItemByCacheId = function (cache_id) {

  function findCacheID(child_elements) {

    var i; // loop counter
    var max; // loop control
    var le;
    var res;

    max = child_elements.length;
    for (i = 0; i < max; i++) {
      le = child_elements[i];
      if (le.cache_id === cache_id) {
        return le;
      }
      else {
        res = findCacheID(le.child_cache_elements);
        if (res) return res;
      }
    }
      
    return null;
  }

  return findCacheID(this.child_cache_elements);
  
};

/**
 * @method emptyCache
 *
 * @memberOf OpenAjax.a11y.cache.ListsCache
 *
 * @desc Empties all the properties of the list cache 
 */

OpenAjax.a11y.cache.ListsCache.prototype.emptyCache = function () {

  this.dom_cache = null;
  this.up_to_date = false;
  
  this.child_elements     = []; 

  this.container_elements = [];  
  this.length = 0;

  this.sort_property  = 'document_order';
  this.sort_ascending = true;
};


/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.ListsCache
 *
 * @desc Returns a text string representation of the lists cache object 
 *
 * @return {String} Returns string represention the lists cache object
 */

OpenAjax.a11y.cache.ListsCache.prototype.toString = function () {

 var str ="\n\nList Information\n";

 var list_length = this.container_elements.length;
 
 for (var i=0; i < list_length; i++ ) {
  str += this.container_elements[i].toString();  
 } // end loop

 return str;
};

/* ---------------------------------------------------------------- */
/*                            ListElement                           */
/* ---------------------------------------------------------------- */

/**
 * @constructor ListElement
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Cache object to be inserted into ListsCache; corresponds to
 *       either a LI, DT, DD element in the DOM
 *
 * @param  {DOMelement}        dom_element       - The dom element object representing the input element 
 * @param  {ContainerElement}  parent_container  - Reference to the container element the list element is contained in
 *
 * @property  {DOMElement}  dom_element     - Reference to the dom element representing the list element
 * @property  {String}      cache_id        - String that uniquely identifies the cache element object in the cache
 * @property  {Number}      document_order  - Ordinal position of the list element in the document
 *
 * @property  {ContainerElement}  parent_container  - Reference to the container element the list element is contained in
 * @property  {Number}            list_type         - Type of list cache element object
 *
 * @property  {Array}   child_cache_elements  - Array of child cache list elements as part of list cache tree 
 *
 * @property  {Number}  link_count    - Number of links in this list element
 */

OpenAjax.a11y.cache.ListElement = function (dom_element, parent_container) {

  this.dom_element    = dom_element;
  this.cache_id       = "";
  this.document_order = 0;
  
  this.parent_container  = parent_container;
  this.list_type = OpenAjax.a11y.LIST.ITEM;

  this.child_cache_elements = [];
  
  this.link_count = 0;

};

/**
 * @method addChildElement
 *
 * @memberOf OpenAjax.a11y.cache.ListElement
 *
 * @desc Add a list element object to the tree of list cache items 
 *
 * @param {ContainerElement | ListElement | LandmarkElement } list_element - list cache element object to add to the list cache
 *
 * @return {boolean} indicating success or failure
 */

OpenAjax.a11y.cache.ListElement.prototype.addChildElement = function (list_element) {

  if (list_element) {
    this.child_cache_elements.push(list_element);
    return true;
  }

  return false;

};

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.ListElement
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.ListElement.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.ListElement
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style display objects
 */

OpenAjax.a11y.cache.ListElement.prototype.getStyle = function () {

  return this.dom_element.getStyle();
  
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.ListElement
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of attribute display object
 */

OpenAjax.a11y.cache.ListElement.prototype.getAttributes = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  var attributes = this.dom_element.getAttributes();
  
  if (!unsorted) this.dom_element.sortItems(attributes);
  
  return attributes;
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.ListElement
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of cache property display object
 */

OpenAjax.a11y.cache.ListElement.prototype.getCacheProperties = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;

  var properties = this.dom_element.getCacheProperties(unsorted);

  cache_nls.addPropertyIfDefined(properties, this, 'list_type');

  if (!unsorted) this.dom_element.sortItems(properties);

  return properties;
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.ListElement
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.ListElement.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};

/**
 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.ListElement
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event item display objects
 */

OpenAjax.a11y.cache.ListElement.prototype.getEvents = function () {
   
  return this.dom_element.getEvents();
  
};



/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.ListElement
 *
 * @desc Returns a text string representation of the list item object 
 *
 * @return {String} Returns string represention the list item object
 */

OpenAjax.a11y.cache.ListElement.prototype.toString = function () {

 return "List Item " + this.document_order + ": " + this.dom_element.getText(); 
 
};

/* ---------------------------------------------------------------- */
/*                           ContainerElement                       */
/* ---------------------------------------------------------------- */

/**
 * @constructor ContainerElement
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Cache object to be inserted into ListsCache; corresponds to
 *       either a OL, UL, DL element in the DOM
 *
 * @param  {DOMelement}  dom_element  - The dom element object representing the input element 
 * @param  {ListInfo}    list_info    - Current list information about 
 *
 * @property  {DOMElement}  dom_element     - Reference to the dom element representing the container element
 * @property  {String}      cache_id        - String that uniquely identifies the cache element object in the cache
 * @property  {Number}      document_order  - Ordinal position of the container element in the document
 *
 * @property  {ContainerElement}  parent_container  - Reference to the container element the container element is contained in
 * @property  {LandmarkElement}   parent_landmark   - Reference to the landmark element the container element is contained in
 * @property  {Number}            list_type         - Type of list cache element object
 *
 * @property  {Array}   child_cache_elements  - Array of child cache list elements as part of list cache tree 
 *
 * @property  {Number}  links_count           - Number list elements that are links
 * @property  {Number}  links_in_domain       - Number of links that are in the same domain of the page
 *
 * @property  {Array}  list_elements          - Array of list elements
 * @property  {Number} length                 - Number of list elements in the container 
 */

OpenAjax.a11y.cache.ContainerElement = function (dom_element, list_info) {

  this.dom_element    = dom_element;
  this.cache_id       = "";
  this.document_order = 0;
  
  this.parent_container  = list_info.parent_container;
  this.parent_landmark   = list_info.parent_landmark;
  
  this.list_type = OpenAjax.a11y.LIST.CONTAINER;

  this.child_cache_elements = [];
  
  this.link_count     = 0;
  this.link_in_domain = 0;

  this.list_elements = [];
  this.length = 0;

};

/**
 * @method addListElement
 *
 * @memberOf OpenAjax.a11y.cache.ContainerElement
 *
 * @desc Add a list element object to the list of list items 
 *
 * @param {ListElement} list_element - list element object to add to the list of list elements
 *
 * @return {boolean} indicating success or failure
 */

OpenAjax.a11y.cache.ContainerElement.prototype.addListElement = function (list_element) {

  if (list_element) {
    this.length += 1;
    list_element.document_order = this.length;
    list_element.cache_id = this.cache_id + "_li_" + this.length;
    this.list_elements.push(list_element);
    return true;
  }

  return false;

};


/**
 * @method addChildElement
 *
 * @memberOf OpenAjax.a11y.cache.ContainerElement
 *
 * @desc Add a list element object to the tree of list cache items 
 *
 * @param {ContainerElement | ListElement | LandmarkElement } list_element - list cache element object to add to the list cache
 *
 * @return {boolean} indicating success or failure
 */

OpenAjax.a11y.cache.ContainerElement.prototype.addChildElement = function (list_element) {

  if (list_element) {
    this.child_cache_elements.push(list_element);
    return true;
  }

  return false;

};

/**
 * @method isListOfLinks
 *
 * @memberOf OpenAjax.a11y.cache.ContainerElement
 *
 * @desc Check whether a list container contains at least the
 *       minimum number of li elements with one and only one link.
 *
 * @param {Number}  min_li  The minimum number of list elements with one link
 *                          that the list element must contain.
 *
 * @return {boolean} Returns true if the list is considered a list of links, otherwise false
 */

OpenAjax.a11y.cache.ContainerElement.prototype.isListOfLinks = function (min_li) {
 
  var child_elements = this.child_cache_elements;
  var max = child_elements.length;
  var i;  // loop counter
  var ce; // loop placeholder

  // results
  var count_li = 0;
  var count_li_with_link = 0;

  for (i = 0; i < max; i++) {
    ce = child_elements[i];

    // ignore elements that are not 'li'
    if (ce.list_type !== OpenAjax.a11y.LIST.ITEM) continue;

    // we've got an 'li' element
    count_li += 1;

    // but each must have a link_count of 1
    if (ce.link_count != 1) return false;
    count_li_with_link += 1;
  }

  return (count_li == count_li_with_link) && (count_li >= min_li);

};

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.ContainerElement
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.ContainerElement.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.ContainerElement
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style display objects
 */

OpenAjax.a11y.cache.ContainerElement.prototype.getStyle = function () {

  return this.dom_element.getStyle();
  
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.ContainerElement
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of attribute display object
 */

OpenAjax.a11y.cache.ContainerElement.prototype.getAttributes = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  var attributes = this.dom_element.getAttributes();
  
  return attributes;
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.ContainerElement
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of cache property display object
 */

OpenAjax.a11y.cache.ContainerElement.prototype.getCacheProperties = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;

  var properties = this.dom_element.getCacheProperties(unsorted);
  
  cache_nls.addPropertyIfDefined(properties, this, 'list_type');
  cache_nls.addPropertyIfDefined(properties, this, 'link_count');
 
  if (!unsorted) this.dom_element.sortItems(properties);

  return properties;
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.ContainerElement
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.ContainerElement.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};

/**

 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.ContainerElement
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event item display objects
 */

OpenAjax.a11y.cache.ContainerElement.prototype.getEvents = function () {
   
  return this.dom_element.getEvents();
  
};

/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.ContainerElement
 *
 * @desc Returns a text string representation of the container element object 
 *
 * @return {String} Returns string represention the container element object
 */

OpenAjax.a11y.cache.ContainerElement.prototype.toString = function () {

 return "List Container " + this.document_order + " with " + this.child_cache_elements.length + " list items"; 
 
};



/*
 * Copyright 2011-2013 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* ---------------------------------------------------------------- */
/*                      OpenAjax Media Cache                        */ 
/* ---------------------------------------------------------------- */

/**
 * @constructor MediaInfo
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates a MediaInfo object for preserving the current media information 
 *        when traversing the DOM for audio and video information
 *
 * @param {MediaInfo} media_info - Current MediaInfo object
 *
 * @property {MediaElement}   media_element  - Parent MediaElement (if any)
 */

OpenAjax.a11y.cache.MediaInfo = function (media_info) {
 
 if (media_info) {
  this.media_element  = media_info.media_element;
 }
 else {
  this.media_element  = null;
 } 
}; 



/**
 * @constructor MediaCache
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates cache object representing information related to audio, video and other media objects in a document
 *
 * @param {DOMCache}   dom_cache   - Reference to the DOMCache object 
 * 
 * @property {DOMCache} dom_cache  - Reference to the DOMCache object 
 *         
 * @property {Boolean}  up_to_date - Boolean true if the cache has been creating using the current DOMElements, else false
 *                                   NOTE: This is a common property of all caches and is used when selectively build caches 
 *                                         based on whether a rule needs the cache
 *
 * @property {Array}    media_elements  - List of media element objects in the document 
 * @property {Number}   length          - Number of media element objects in the list 
 *
 * @property {String}   sort_property   - Image element object property the list of media objects is sorted by
 * @property {Boolean}  sort_ascending  - true if list is sorted in ascending order, otherwise false 
 *
 * @property {ResultRuleSummary}  rule_summary_result  - Rule results associated with this cache
 */

OpenAjax.a11y.cache.MediaCache = function (dom_cache) {

  this.dom_cache = dom_cache;
  this.up_to_date = false;
  
  this.media_elements = [];
  this.object_elements = [];
  this.video_elements = [];
  this.audio_elements = [];
  this.embed_elements = [];
  this.length = 0;
 
  this.sort_property = 'document_order';
  this.sort_ascending = false;
 
}; 

/**
 * @method addMediaElement
 * 
 * @memberOf OpenAjax.a11y.cache.MediaCache
 *
 * @desc Adds a media element object to the list of media elements and generates a cache id for the object.
 *
 * @param  {MediaElement}  media_element  - media element object to add 
 *
 * @return {Number} Returns the length of the list of media element objects
 */

OpenAjax.a11y.cache.MediaCache.prototype.addMediaElement = function ( media_element ) {

  // item must exist and have the position property
  if (media_element) {
    this.length = this.length + 1;
    media_element.cache_id = "media_" + this.length; 
    media_element.document_order = this.length;
    this.media_elements.push( media_element );
    
    if (media_element.dom_element.tag_name === 'video')  this.video_elements.push(media_element);
    if (media_element.dom_element.tag_name === 'audio')  this.audio_elements.push(media_element);
    if (media_element.dom_element.tag_name === 'object') this.object_elements.push(media_element);
    if (media_element.dom_element.tag_name === 'embed')  this.embed_elements.push(media_element);

 } 

 return this.length;

};

/**
 * @method getMediaElementByCacheId
 * 
 * @memberOf OpenAjax.a11y.cache.MediaCache
 *
 * @desc Finds the the media element object with the matching cache id
 *
 * @param  {String}  cache_id  - Cache id of media element object
 *
 * @return {MediaElement | null} Returns cache media element object if cache id is found, otherwise null
 */

OpenAjax.a11y.cache.MediaCache.prototype.getMediaElementByCacheId = function (cache_id) {
  return this.getItemByCacheId(cache_id);
};

/**
 * @method getItemByCacheId
 * 
 * @memberOf OpenAjax.a11y.cache.MediaCache
 *
 * @desc Finds the the media element object with the matching cache id
 *
 * @param  {String}  cache_id  - Cache id of media element object
 *
 * @return {MediaElement | null} Returns cache media element object if cache id is found, otherwise null
 */

OpenAjax.a11y.cache.MediaCache.prototype.getItemByCacheId = function (cache_id) {

  var i;
  var media_elements_len = this.media_elements.length;

  if (cache_id) {  
    for (i=0; i < media_elements_len; i++) {
      if (this.media_elements[i].cache_id == cache_id) {
        return this.media_elements[i];
      }
    } // end loop
  } 
  return null;
};

/**
 * @method emptyCache
 *
 * @memberOf OpenAjax.a11y.cache.MediaCache
 *
 * @desc Resests the media cache object properties and empties all the lists and arrays 
 */

OpenAjax.a11y.cache.MediaCache.prototype.emptyCache = function () {

  this.media_elements  = [];
  this.audio_elements  = [];
  this.video_elements  = [];
  this.object_elements = [];
  
  this.sort_property = 'document_order';
  this.sort_ascending = false;
  this.up_to_date = false;

};

/**
 * @method updateCacheItems
 *
 * @memberOf OpenAjax.a11y.cache.MediaCache
 *
 * @desc Updates the media cache by checking to see if a dom element
 *          should be added to the cache
 *  
 * @param  {DOMElement}   dom_element   - dom element object to check for inclusion in media cache
 * @param  {MediaInfo}    media_info  - Information about the current media element relationships in the DOM
 *
 */
 
OpenAjax.a11y.cache.MediaCache.prototype.updateCacheItems = function (dom_element, media_info) {

  var mi = new OpenAjax.a11y.cache.MediaInfo(media_info);
  var media_element;

  if ((dom_element.tag_name === 'object') ||
      (dom_element.tag_name === 'applet') ||
      (dom_element.tag_name === 'embed') ||
      (dom_element.tag_name === 'audio') ||
      (dom_element.tag_name === 'video')) {

    media_element = new OpenAjax.a11y.cache.MediaElement(dom_element);    
    this.dom_cache.media_cache.addMediaElement(media_element);
    
    mi.media_element = media_element;
    
  }
  else {
  
    if ((dom_element.tag_name === 'param') &&
        (media_info.media_element && media_info.media_element.dom_element.tag_name === 'object')) {
       media_element = new OpenAjax.a11y.cache.MediaChildElement(dom_element);    
       media_info.media_element.addMediaElement(media_element);          
    }    
    
    if ((dom_element.tag_name === 'track') &&
        (media_info.media_element && 
         (media_info.media_element.dom_element.tag_name === 'video') || 
         (media_info.media_element.dom_element.tag_name === 'audio'))) {
       media_element = new OpenAjax.a11y.cache.MediaChildElement(dom_element);    
       media_info.media_element.addMediaElement(media_element);          
    }    

  }
  
  return mi;
    
};

/**
 * @method traverseDOMElementsForMediaElements
 *
 * @memberOf OpenAjax.a11y.cache.MediaCache
 *
 * @desc Traverses DOMElement objects in the tree to update the media cache 
 *
 * @param  {DOMElement}  dom_element - dom element object to check for inclusion in media cache
 * @param  {MediaInfo}   media_info  - information about a media elements
 */
 
OpenAjax.a11y.cache.MediaCache.prototype.traverseDOMElementsForMediaElements = function (dom_element, media_info) {

  var i;

  if (!dom_element) return;

  if (dom_element.type == Node.ELEMENT_NODE) {

    var mi = this.updateCacheItems(dom_element, media_info);
  
    for (i=0; i<dom_element.child_dom_elements.length; i++) {
      this.traverseDOMElementsForMediaElements(dom_element.child_dom_elements[i], mi);
    } // end loop
  }  
  
};


/**
 * @method updateCache
 *
 * @memberOf OpenAjax.a11y.cache.MediaCache
 *
 * @desc Traverses the DOMElements to update the media cache
 *       NOTE: This function is only used when the specialized caches
 *       are build as rules need them.  In this condition, if the rules 
 *       dependent on the media cache are disabled, this cache would 
 *       not be updated
 */
 
OpenAjax.a11y.cache.MediaCache.prototype.updateCache = function () {
  var i;
  var children = this.dom_cache.element_cache.child_dom_elements;
  var children_len = children.length;
 
  var media_info = new OpenAjax.a11y.cache.MediaInfo();
 
  for (i=0; i < children_len; i++) {
    this.traverseDOMElementsForMediaElements(children[i], media_info);
  }  

  this.up_to_date = true;
};

/**
 * @method sortMediaElements
 *
 * @memberOf OpenAjax.a11y.cache.MediaCache
 *
 * @desc Sorts media element array by a media element object property
 *
 * @param {String}   property   - Property of media element object to sort the list
 * @param {Boolean}  ascending  - true if sort in ascending order; false in descending order
 *
 * @return {Boolean}  Returns true if list was sorted, false if not
 */

OpenAjax.a11y.cache.MediaCache.prototype.sortMediaElements = function(property, ascending ) {

  var swapped = false;
  var temp = null;
  var i;

  if (this.media_elements && 
      this.media_elements.length && 
      !this.media_elements[0][property] ) {
    return false;
  } // endif

  var media_elements_len = this.media_elements.length;

  if (ascending) {
    do {
      swapped = false;
      for (i=1; i<media_elements_len; i++) {
        if (this.media_elements[i-1][property] > this.media_elements[i][property]) {
          // swap the values
          temp = this.media_elements[i-1];
          this.media_elements[i-1] = this.media_elements[i];
          this.media_elements[i] = temp;
          swapped = true;
        } 
      } // end loop
    } while (swapped);
  }
  else {
    do {
      swapped = false;
      for (i = 1; i < media_elements_len; i++) {
        if (this.media_elements[i-1][property] < this.media_elements[i][property]) {
          // swap the values
          temp = this.media_elements[i-1];
          this.media_elements[i-1] = this.media_elements[i];
          this.media_elements[i] = temp;
          swapped = true;
        } 
      } // end loop
    } while (swapped);
  } 

  this.sort_property = property;

  return true;

};


/* ---------------------------------------------------------------- */
/*                            MediaElement                          */
/* ---------------------------------------------------------------- */

/**
 * @constructor MediaElement
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates media element object representing information related to an object, video, audio, embed or applet element on a web page
 *
 * @param  {DOMelement}   dom_element   - The dom element object representing the media element 
 *
 * @property  {DOMElement}  dom_element     - Reference to the dom element representing the media element
 * @property  {String}      cache_id        - String that uniquely identifies the media element object in the cache
 * @property  {Number}      document_order  - Ordinal position of the media element in the document in relationship to other media elements
 */

OpenAjax.a11y.cache.MediaElement = function (dom_element) {

  this.document_order = 0;
 
  this.dom_element = dom_element;
  
  var node = dom_element.node;
  
  this.child_cache_elements = [];
  
  this.length = 0;
  
  var attributes = node.attributes;
  
  for (var i = 0; i < attributes.length; i++) {

    attr = attributes[i];

    switch (attr.name) {

    case 'controls':
      this.has_controls = true;
      break;  
      
    case 'autoplay':
      this.has_autoplay = true;
      break;  
      
    default:
      break;
      
    }  
      
  }
  
  this.type       = node.getAttribute('type');
  if (typeof type === 'string') type = type.toLowerCase();
  this.data       = node.getAttribute('data');
  this.alt        = node.getAttribute('alt');
  this.longdesc   = node.getAttribute('longdesc');
  this.name       = node.getAttribute('name');
  
  this.file_name = "";
  this.src_is_a_file_name = false;

  if (typeof node.src === 'string'  && node.src.length) {

    this.src        = node.getAttribute('src');

    var pos = this.src.lastIndexOf('/');    
      
    if (pos >= 0 ) this.file_name = this.src.substring((pos+1));
    else this.file_name = this.src; 

    this.src_is_a_file_name = true;
  }
  
};

/**
 * @method addMediaElement
 *
 * @memberOf OpenAjax.a11y.cache.MediaElement
 * 
 * @desc Adds a cache media element to the tree representation of media elements
 *
 * @param  {MediaElement } media_element   - Cache media element object to add 
 */

OpenAjax.a11y.cache.MediaElement.prototype.addMediaElement = function (media_element) {

 if (media_element) {
    this.length = this.length + 1;
    media_element.cache_id = this.cache_id + "_child_" + this.length; 
    media_element.document_order = this.length;    
    this.child_cache_elements.push(media_element); 
 }  

};

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.MediaElement
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.MediaElement.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.MediaElement
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style display objects
 */

OpenAjax.a11y.cache.MediaElement.prototype.getStyle = function () {

  return  this.dom_element.getStyle();
  
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.MediaElement
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of attribute display object
 */

OpenAjax.a11y.cache.MediaElement.prototype.getAttributes = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  var attributes = this.dom_element.getAttributes(unsorted);
  
  cache_nls.addPropertyIfDefined(attributes, this, 'name');
  cache_nls.addPropertyIfDefined(attributes, this, 'controls');
  cache_nls.addPropertyIfDefined(attributes, this, 'autoplay');
  cache_nls.addPropertyIfDefined(attributes, this, 'type');
  cache_nls.addPropertyIfDefined(attributes, this, 'src');
  cache_nls.addPropertyIfDefined(attributes, this, 'file_name');
  cache_nls.addPropertyIfDefined(attributes, this, 'data');
  cache_nls.addPropertyIfDefined(attributes, this, 'alt');
  cache_nls.addPropertyIfDefined(attributes, this, 'longdesc');
  cache_nls.addPropertyIfDefined(attributes, this, 'height');
  cache_nls.addPropertyIfDefined(attributes, this, 'width');

  return attributes;
  
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.MediaElement
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of cache property display object
 */

OpenAjax.a11y.cache.MediaElement.prototype.getCacheProperties = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  var properties = [];
  
  cache_nls.addPropertyIfDefined(properties, this, 'alt_for_comparison');
  cache_nls.addPropertyIfDefined(properties, this, 'document_order');
  
  return properties;
  
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.MediaElement
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.MediaElement.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};


/**
 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.MediaElement
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event item display objects
 */

OpenAjax.a11y.cache.MediaElement.prototype.getEvents = function () {
   
  return this.dom_element.getEvents();
  
};

/**
 * @method getTextTracks
 *
 * @memberOf OpenAjax.a11y.cache.MediaElement
 *
 * @desc Returns an array of media child elements for audio and video elements
 *
 * @return {Array} Returns a array of media child elements
 */

OpenAjax.a11y.cache.MediaElement.prototype.getTextTracks = function () {

  var tt = [];

  for (var i = 0; i < this.child_cache_elements.length; i++) {

    var cce = this.child_cache_elements[i];

    if (cce.dom_element.tag_name === 'track') tt.push(cce);
   
  }
   
  return tt;
  
};

/**
 * @method hasCaptionTrack
 *
 * @memberOf OpenAjax.a11y.cache.MediaElement
 *
 * @desc Returns a boolean value on the availability of the caption track
 *
 * @return {Boolean} Returns a true if media element has a caption track, otherwise false
 */

OpenAjax.a11y.cache.MediaElement.prototype.hasCaptionTrack = function () {

  var tracks = this.getTextTracks();
  
  for (var i = 0; i < tracks.length; i++) {
    var track = tracks[i];    
    if ((track.tag_name === 'track') && 
         (track.kind === 'captions')) return true; 
  }
   
  return false;
  
};

/**
 * @method hasDescriptionTrack
 *
 * @memberOf OpenAjax.a11y.cache.MediaElement
 *
 * @desc Returns a boolean value on the availability of the audio description track
 *
 * @return {Boolean} Returns a true if media element has a audio description track, otherwise false
 */

OpenAjax.a11y.cache.MediaElement.prototype.hasDescriptionTrack = function () {

  var tracks = this.getTextTracks();
  
  for (var i = 0; i < tracks.length; i++) {
    var track = tracks[i];    
    if ((track.tag_name === 'track') && 
        (track.kind === 'descriptions')) return true; 
  }
   
  return false;
  
};

/**
 * @method hasSubtitleTrack
 *
 * @memberOf OpenAjax.a11y.cache.MediaElement
 *
 * @desc Returns a boolean value on the availability of the sub title track
 *
 * @return {Boolean} Returns a true if media element has a sub title track, otherwise false
 */

OpenAjax.a11y.cache.MediaElement.prototype.hasSubtitleTrack = function () {

  var tracks = this.getTextTracks();
  
  for (var i = 0; i < tracks.length; i++) {
    var track = tracks[i];    
    if ((track.tag_name === 'track') && 
        (track.kind === 'subtitles')) return true; 
  }
   
  return false;
  
};

/**
 * @method isTypeVideo
 *
 * @memberOf OpenAjax.a11y.cache.MediaElement
 *
 * @desc Returns a boolean value on if the type attribute is a 'video' type
 *
 * @return {Boolean} Returns a true if 'video' type, otherwise false
 */

OpenAjax.a11y.cache.MediaElement.prototype.isTypeVideo = function () {

  if ((typeof this.type === 'string') && 
      (this.type.indexOf('video') >= 0)) return true;
   
  return false;
  
};

/**
 * @method isTypeAudio
 *
 * @memberOf OpenAjax.a11y.cache.MediaElement
 *
 * @desc Returns a boolean value on if the type attribute is a 'audio' type
 *
 * @return {Boolean} Returns a true if 'audio' type, otherwise false
 */

OpenAjax.a11y.cache.MediaElement.prototype.isTypeAudio = function () {

  if ((typeof this.type === 'string') && 
      (this.type.indexOf('audio') >= 0)) return true;
   
  return false;
  
};
/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.MediaElement
 *
 * @desc Creates a text string representation of the media element object 
 *
 * @return {String} Returns a text string representation of the media element object
 */
 
 OpenAjax.a11y.cache.MediaElement.prototype.toString = function () {
   if (typeof this.type       === 'string' && this.type.length) return this.dom_element.tag_name + ": " + this.type;
   if (this.file_name.length) return this.dom_element.tag_name + ": " + this.file_name;
   return this.dom_element.tag_name;
 };


/* ---------------------------------------------------------------- */
/*                            MediaChildElement                          */
/* ---------------------------------------------------------------- */

/**
 * @constructor MediaChildElement
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates media child element object representing possible caption and audio description information related to an object, video, audio on a web page
 *
 * @param  {DOMelement}   dom_element   - The dom element object representing the media element 
 *
 * @property  {DOMElement}  dom_element     - Reference to the dom element representing the media element
 * @property  {String}      cache_id        - String that uniquely identifies the media element object in the cache
 * @property  {Number}      document_order  - Ordinal position of the media element in the document in relationship to other media elements
 */

OpenAjax.a11y.cache.MediaChildElement = function (dom_element) {

  this.document_order = 0;
  this.cache_id = "";
 
  this.dom_element = dom_element;
  var node = dom_element.node;

  this.tag_name     = dom_element.tag_name;
  this.name         = node.getAttribute('name');
  this.value        = node.getAttribute('value');
  this.src          = node.getAttribute('src');
  this.kind         = node.getAttribute('kind');
  this.srclang      = node.getAttribute('srclang');
  this.label        = node.getAttribute('label');
  this.default_attr = node.getAttribute('default');
  

};

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.MediaChildElement
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.MediaChildElement.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.MediaChildElement
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style display objects
 */

OpenAjax.a11y.cache.MediaChildElement.prototype.getStyle = function () {

  return  this.dom_element.getStyle();
  
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.MediaChildElement
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of attribute display object
 */

OpenAjax.a11y.cache.MediaChildElement.prototype.getAttributes = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  var attributes = [];
  
  cache_nls.addPropertyIfDefined(attributes, this, 'name');
  cache_nls.addPropertyIfDefined(attributes, this, 'value');
  cache_nls.addPropertyIfDefined(attributes, this, 'src');
  cache_nls.addPropertyIfDefined(attributes, this, 'kind');
  cache_nls.addPropertyIfDefined(attributes, this, 'srclang');
  cache_nls.addPropertyIfDefined(attributes, this, 'label');
  cache_nls.addPropertyIfDefined(attributes, this, 'default_attr');
   
  return attributes;
   
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.MediaChildElement
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of cache property display object
 */

OpenAjax.a11y.cache.MediaChildElement.prototype.getCacheProperties = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  var properties = [];
    
  return properties;
  
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.MediaChildElement
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.MediaChildElement.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};


/**
 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.MediaChildElement
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event item display objects
 */

OpenAjax.a11y.cache.MediaChildElement.prototype.getEvents = function () {
   
  return this.dom_element.getEvents();
  
};

/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.MediaChildElement
 *
 * @desc Creates a text string representation of the media element object 
 *
 * @return {String} Returns a text string representation of the media element object
 */
 
 OpenAjax.a11y.cache.MediaChildElement.prototype.toString = function () {
 
   if ((this.tag_name === 'track') && 
       (typeof this.kind === 'string') &&
       this.kind.length) return this.dom_element.tag_name + ": " + this.kind;

   return this.dom_element.tag_name;

 };


/*
 * Copyright 2011-2013 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

 
/**
 * @constructor DOMElementComputedStyle
 *
 * @memberOf OpenAjax.a11y.cache
 * 
 * @desc Create a dom element computed style object is used to add style properties to dom element cache objects 
 * 
 * @param  {DOMElement}  dom_element     - dom element node to add computed style information to 
 * @param  {DOMElement}  parent_element  - parent dom element node for computing inherited properties 
 *
 * @property  {String}  display      - Computed value of the CSS 'display' property
 * @property  {String}  visibility   - Computed value of the CSS 'visibility' property
 * @property  {Boolean} aria_hidden  - aria-hidden property
 *
 * @property  {Number}  is_visible_onscreen   - Constant representing the graphical visibility of the element (i.e is it visible to people with sight)
 * @property  {Number}  is_visible_to_at      - Constant representing the assistive technology visibility of the element (i.e is it visible to people using a screen reader)
 * 
 * @property  {String}  color                 - Computed value of the CSS 'color' property
 * @property  {String}  color_hex             - Computed value of the CSS 'color' property in hexidecimal format
 * @property  {String}  opacity               - Computed value of the CSS 'opacity' property
 * @property  {String}  background_color      - Computed value of the CSS 'background-color' property
 * @property  {String}  background_color_hex  - Computed value of the CSS 'background-color' property in hexidecimal format
 * @property  {String}  background_image      - Computed value of the CSS 'background-image' property
 * @property  {String}  background_repeat     - Computed value of the CSS 'background-repeat' property
 * @property  {String}  background_position   - Computed value of the CSS 'background-position' property
 *
 * @property  {String}  font_family  - Computed value of the CSS 'font-family' property
 * @property  {String}  font_size    - Computed value of the CSS 'font-size' property
 * @property  {String}  font_weight  - Computed value of the CSS 'font-weight' property
 *
 * @property  {String}  position  - Computed value of the CSS 'position' property
 * @property  {String}  left      - Computed value of the CSS 'left' property  
 * @property  {String}  top       - Computed value of the CSS 'top' property
 * @property  {String}  width     - Computed value of the width of the rendered element in pixels  
 * @property  {String}  height    - Computed value of the height of the rendered element in pixels
 */
 
OpenAjax.a11y.cache.DOMElementComputedStyle = function (dom_element, parent_element) {

  function normalizeBackgroundImage(value, parent_element) {

    var v = value;

    if ((value.toLowerCase() === 'inherit') || 
        (value.toLowerCase() === 'none') || 
        (value === '')) {
    
      if (parent_element) {  
        v = parent_element.computed_style.background_image;
      }
      else {
        v = 'none';
      }
    }  
  
    return v;
  
  } // end function

  function  normalizeFontSize(value, parent_element) {
    if (value.toLowerCase() == 'inherit') {
      if (parent_element) {
        return parent_element.computed_style.font_size;
      }
      else {
        return 12;
      }    
    }
    else {
      return value;
    }
  } // end function

  function  normalizeFontWeight(value, parent_element) {
    if (isNaN(value) ) {
      switch (value.toLowerCase()) {
      case 'bold':
        return 700;

      case 'normal':
        return 400;

      case 'inherit':
        if (parent_element) {
          return parent_element.computed_style.font_weight;
        }
        else {
          return 400;
        }    

      case 'bolder':
        return 700;
    
      default:
        return 400;
      }
    }
    else {
      return parseInt(value,10);
    }
  } // end function


  function  normalizePositionTop(value, parent_element) {
    if (value.toLowerCase() == 'inherit') {
      if (parent_element) {
        return parent_element.computed_style.top;
      }
      else {
        return 0;
      }    
    }
    else {
      return parseInt(value,10);
    }
  } // end function

  function  normalizePositionLeft(value, parent_element) {
    if (value.toLowerCase() == 'inherit') {
      if (parent_element) {
        return parent_element.computed_style.left;
      }
      else {
        return 0;
      }    
    }
    else {
      return parseInt(value,10);
    }
  } // end function

  this.display  = "";
  this.visibility = "";
  this.aria_hidden = false;

  this.is_visible_onscreen = OpenAjax.a11y.VISIBILITY.UNKNOWN;  
  this.is_visible_to_at = OpenAjax.a11y.VISIBILITY.UNKNOWN;

  this.color   = "";
  this.background_color = "";
  this.background_image = "";
  this.font_family = "";
  this.font_size  = "";
  this.font_weight = "";
  this.position  = "";
  this.left    = "";
  this.top     = "";
 
  // check to see if getComputedStyle is defined for the engine 
  if (!window.getComputedStyle) return;

//  OpenAjax.a11y.logger.info("Element: " + dom_element );  

  var style = window.getComputedStyle(dom_element.node, null);  
   
  this.display    = style.getPropertyValue("display");
  this.visibility = style.getPropertyValue("visibility");
 
  this.color               = style.getPropertyValue("color");
  this.opacity             = style.getPropertyValue("opacity");
  this.background_color    = style.getPropertyValue("background-color");
  this.background_image    = normalizeBackgroundImage(style.getPropertyValue("background-image"), parent_element);
  this.background_repeat   = style.getPropertyValue("background-repeat");
  this.background_position = style.getPropertyValue("background-position");
 
  this.font_family = style.getPropertyValue("font-family");  
  this.font_size   = normalizeFontSize(style.getPropertyValue("font-size"), parent_element); 
  this.font_weight = normalizeFontWeight(style.getPropertyValue("font-weight"), parent_element); 
 
  this.position = style.getPropertyValue("position");

  // test if getBoundingClientRect is supported 
  if (dom_element.node.getBoundingClientRect) {
    var client_rect = dom_element.node.getBoundingClientRect();
    this.client_rect = client_rect;
    if (client_rect) {
      this.top     = client_rect.top;
      this.left    = client_rect.left;
      this.height  = client_rect.height;
      this.width   = client_rect.width; 
    }  
    else {
      this.top  = normalizePositionTop(style.getPropertyValue("top"), parent_element);
      this.left = normalizePositionLeft(style.getPropertyValue("left"), parent_element);
    }
  }
  else {
    this.top     = normalizePositionTop(style.getPropertyValue("top"), parent_element);
    this.left    = normalizePositionLeft(style.getPropertyValue("left"), parent_element);
  }
 
  // This is an edge case test typcially for body elements and frames
  if ((this.background_color == 'inherit') ||
      (this.background_color == 'transparent')) {
    if (parent_element && parent_element.computed_style) { 
      this.background_color   = parent_element.computed_style.background_color;
      this.background_color_hex = parent_element.computed_style.background_color_hex;
    }
    else {
      this.background_color = 'rgb(255,255,255)';
      this.background_color_hex = 'ffffff';
    }   
  } 
  else {
    this.background_color_hex = OpenAjax.a11y.util.RGBToHEX(style.getPropertyValue("background-color")); 
  }

  if (parent_element && 
      parent_element.computed_style ) {

    var parent_style = parent_element.computed_style;

    // We do have parent_element so use its information if needed  
 
    if ((this.display === 'inherit') ||  
        (parent_element.computed_style.display == 'none')) {
      this.display = 'none';
    } 

    if ((this.visibility === 'inherit') ||
        (parent_style.visibility === 'hidden')) {
      this.visibility = parent_style.visibility;
    } 

    if (this.color == 'inherit') {
      this.color = parent_style.color;
      this.color_hex = parent_style.color_hex;
    }
    else {
      this.color_hex = OpenAjax.a11y.util.RGBToHEX(style.getPropertyValue("color"));
    }
    
    if (this.font_family === 'inherit') {
      this.font_family = parent_style.font_family;
    } 
  
    if (this.position === 'inherit') {
      this.position = parent_style.position;
    } 
  } 
 
  // Calcuate visibility of node content in graphical renderings and to assistive technologies

  if (this.visibility && 
      this.visibility.length && 
      this.display && 
      this.display.length ) { 
      
    if ((this.visibility === 'hidden') ||
        (this.display === 'none')) {
        
      if (dom_element.tag_name !== 'area') {
        this.is_visible_onscreen = OpenAjax.a11y.VISIBILITY.HIDDEN;    
        this.is_visible_to_at    = OpenAjax.a11y.VISIBILITY.HIDDEN;
      }
      else {
        this.is_visible_onscreen = OpenAjax.a11y.VISIBILITY.VISIBLE;    
        this.is_visible_to_at    = OpenAjax.a11y.VISIBILITY.VISIBLE;     
      }
      
    } 
    else {
      if ((parseInt(this.top,10) < 0) || (parseInt(this.left,10) < 0)) {
        this.is_visible_onscreen = OpenAjax.a11y.VISIBILITY.HIDDEN;
      }
      else {
        this.is_visible_onscreen = OpenAjax.a11y.VISIBILITY.VISIBLE;
      }
   
      if (((typeof dom_element.aria_hidden === 'string') && 
          (dom_element.aria_hidden === "true")) ||
          (parent_style && parent_style.aria_hidden)) {
        this.aria_hidden = true;  
        this.is_visible_to_at = OpenAjax.a11y.VISIBILITY.HIDDEN;  
      } 
      else {
        this.is_visible_to_at = OpenAjax.a11y.VISIBILITY.VISIBLE;     
      }
    }
  } 

  this.is_large_font = (parseInt(this.font_size,10) >= 18) || ((parseInt(this.font_size,10) >= 14) && (parseInt(this.font_weight,10) >= 300));

};

/**
 * @method calculateColorContrast
 *  
 * @memberOf OpenAjax.a11y.cache.DOMElementComputedStyle
 * 
 * @desc Calculates a color contrast raio (CCR) value for the element style object 
 *
 * @return {Number}  Returns a number representing the color contrast ratio (CCR)
 */ 

OpenAjax.a11y.cache.DOMElementComputedStyle.prototype.calculateColorContrastRatio = function () {

 if( this.color_hex && 
   (this.color_hex.length == 6) && 
    this.background_color_hex && 
   (this.background_color_hex.length == 6)) {
  var L1 = this.getLuminance(this.color_hex);
  var L2 = this.getLuminance(this.background_color_hex);
  this.color_contrast_ratio = Math.round((Math.max(L1, L2) + 0.05)/(Math.min(L1, L2) + 0.05)*10)/10;
 }
 else {
  this.color_contrast_ratio = null;
 }

 return this.color_contrast_ratio;
   
};


/**
 * @method getLuminance
 *
 * @memberOf OpenAjax.a11y.cache.DOMElementComputedStyle
 * 
 * @desc Get the luminance value of a hex incoded color 
 *
 * @param {String}  color  - Hex representation of a CSS color value
 * 
 * @return {Number}  Returns a number representing the limnance value
 */ 

OpenAjax.a11y.cache.DOMElementComputedStyle.prototype.getLuminance = function (color) {

 // OpenAjax.a11y.logger.debug("  " + color );

 // Get decimal values
 var R8bit = parseInt(color.substring(0,2),16);
 var G8bit = parseInt(color.substring(2,4),16);
 var B8bit = parseInt(color.substring(4,6),16);
        
 // Get sRGB values
 var RsRGB = R8bit/255;
 var GsRGB = G8bit/255;
 var BsRGB = B8bit/255;
  // Calculate luminance
 var R = (RsRGB <= 0.03928) ? RsRGB/12.92 : Math.pow(((RsRGB + 0.055)/1.055), 2.4);
 var G = (GsRGB <= 0.03928) ? GsRGB/12.92 : Math.pow(((GsRGB + 0.055)/1.055), 2.4);
 var B = (BsRGB <= 0.03928) ? BsRGB/12.92 : Math.pow(((BsRGB + 0.055)/1.055), 2.4);
			
 return (0.2126 * R + 0.7152 * G + 0.0722 * B);
			
};

/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.DOMElementComputedStyle
 *
 * @desc Creates a text string representation of the computed style object 
 *
 * @return {String} Returns a text string representation of the computed style object
 */ 

OpenAjax.a11y.cache.DOMElementComputedStyle.prototype.toString = function (color) {
  return "Computed style " + this.color_hex + " " + this.background_color_hex + " " + this.color_contrast_ratio; 
};
/*
 * Copyright 2011-2013 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* ---------------------------------------------------------------- */
/*                            TableInfo                             */
/* ---------------------------------------------------------------- */

/**
 * @constructor TableInfo
 * 
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates a TableInfo object for preserving the current table information 
 *        when traversing the DOM for table information
 *
 * @property {table cache object}   parent_element     - Parent table cache Object (if any)
 * @property {TableElement}         table_element      - Parent TableElement (if any)
 * @property {TBodyElement}         table_body_element - Parent TBodyElement (if any)
 * @property {TableRowElement}      table_row_element  - Parent TableRowElement (if any)
 * 
 * @param {TableInfo} table_info - Current ControlInfo object
 */
 
 OpenAjax.a11y.cache.TableInfo = function (table_info) {

   if (table_info) {
     this.parent_element      = table_info.parent_element;
     this.table_element       = table_info.table_element;
     this.table_body_element  = table_info.table_body_element;
     this.table_row_element   = table_info.table_row_element;
   }
   else {
     this.parent_element      = null;
     this.table_element       = null;
     this.table_body_element  = null;
     this.table_row_element   = null;
   }  
 }; 
 
/* ---------------------------------------------------------------- */
/*                          TablesCache Object                      */
/* ---------------------------------------------------------------- */

/** 
 * @constructor TablesCache
 * 
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Create a table cache object to hold information about tables in a web page
 *          
 * @param {DOMCache}   dom_cache   - Reference to the DOMCache object 
 * 
 * @property {DOMCache}  dom_cache   - Reference to the DOMCache object 
 * @property {Boolean}   up_to_date  - true if the cache has been creating using the current DOMElements, else false
 *                                       NOTE: This is a common property of all caches and is used when selectively build caches 
 *                                             based on whether a rule needs the cache
 *
 * @property {Array}    child_cache_elements - Root array of the tree representation of the table elements in the document 
 *
 * @property {Array}    table_elements - Array of all the TableElement objects in the cache  
 * @property {Number}   length         - Running length of the table_elements array for use in calculating cache_id values
 * 
 * @property {Array}    rule_results   - Root array of the tree representation of the table elements in the document 
 *
 * @property {ResultRuleSummary}  rule_summary_result  - Rule results associated with this cache 
 */
OpenAjax.a11y.cache.TablesCache = function (dom_cache) {

  // Private properties
  this.dom_cache = dom_cache;
  this.up_to_date = false;

  // Public properties
  this.child_cache_elements = [];    
   
  this.table_elements = [];  
  this.length         = 0;  

  this.page_element  = null;  


};

/**
 * @method addTableElement
 * 
 * @memberOf OpenAjax.a11y.cache.TablesCache
 *
 * @desc Adds a table element object to the list of tables and generates a cache_id for the table element object
 *
 * @param {TableElement}  table_element   - TableElement object to add to the cache
 *
 * @return {Number} Returns the number of table element objects in the list
 *
 */
 OpenAjax.a11y.cache.TablesCache.prototype.addTableElement = function (table_element) {

   // item must exist and have the position property
   if (table_element) {
     this.length = this.length + 1;
     table_element.document_order = this.length;
     table_element.cache_id = "table_" + this.length;
     this.table_elements.push( table_element );
   } 

   return this.length;

 };

/** 
 * @method addChild
 * 
 * @memberOf OpenAjax.a11y.cache.TablesCache
 * 
 * @desc Adds a cache table element to the tree representation of the table in the table cache
 *
 * @param  {TableElement | CaptionElement | THeadElement | TBodyElement | TableRowElement | TableCellElement }  table_element  - Cache table element object to add to root of tree of table elements 
 */
 
 OpenAjax.a11y.cache.TablesCache.prototype.addChild = function (table_element) {

   if (table_element) {
     this.child_cache_elements.push(table_element); 
   }  
 }; 

/** 
 * @method addRuleResult
 * 
 * @memberOf OpenAjax.a11y.cache.TablesCache
 * 
 * @desc Add a RuleResult reference to the table cache 
 *
 * @param {RuleResult}  rule_result - Rule result to associate with the table cache 
 */
 OpenAjax.a11y.cache.TablesCache.prototype.addRuleResult = function (rule_result) {

   if (rule_result) {
     this.rule_results.push(rule_result); 
   }  
 }; 

/**
 * @deprecated getTableElementByCacheId
 * 
 * @memberOf OpenAjax.a11y.cache.TablesCache
 *
 * @desc Finds the the table cache element object with the matching cache id
 *
 * @param  {String}  cache_id  - Cache id of table cache element object
 *
 * @return {TableElement | CaptionElement | THeadElement | TBodyElement | TableRowElement | TableCellElement | null} Returns cache table element object if cache id is found, otherwise null
 */
 OpenAjax.a11y.cache.TablesCache.prototype.getTableElementByCacheId = function (cache_id) {
   return this.getItemByCacheId(cache_id);
 };

/**
 * @method getItemByCacheId
 * 
 * @memberOf OpenAjax.a11y.cache.TablesCache
 *
 * @desc Finds the the table cache element object with the matching cache id
 *
 * @param  {String}  cache_id  - Cache id of table cache element object
 *
 * @return {TableElement | CaptionElement | THeadElement | TBodyElement | TableRowElement | TableCellElement | null} Returns cache table element object if cache id is found, otherwise null
 */
 OpenAjax.a11y.cache.TablesCache.prototype.getItemByCacheId = function (cache_id) {

   var i;
   var te;
   var table_elements_len = this.table_elements.length;
   var id_info = cache_id.split('_');
   var table_id = "table_" + id_info[1];
   
   for (i = 0; i < table_elements_len; i++) {
     te = this.table_elements[i];

     if (te.cache_id == cache_id) {
       return te;
     }
     else {
       if (te.cache_id == table_id) {
         return te.getTableElementByCacheId(cache_id);
       }
     }
   }
     
   return null;
 };

/**
 * @method getRuleResultByCacheId
 * 
 * @memberOf OpenAjax.a11y.cache.TablesCache
 *
 * @desc Finds the the rule result object with the matching cache id
 *
 * @param  {String}  cache_id  - Cache id of table cache element object
 *
 * @return {ResultRule | null} Returns cache rule result object if cache id is found, otherwise null
 */
 OpenAjax.a11y.cache.TablesCache.prototype.getRuleResultByCacheId = function (cache_id) {

   var i;
   var rr;
   var rule_results     = this.evaluation_results.rule_results;
   var rule_results_len = rule_results.length;
      
   for (i = 0; i < rule_results_len; i++) {
     rr = rule_results[i];
     if (rr.cache_id == cache_id) return rr;
   } // end loop
     
   return null;
 };
/**
 * @method getRuleResultByRuleId
 *
 * @memberOf OpenAjax.a11y.cache.TablesCache
 *
 * @desc Gets the rule result object with the matching rule id
 *
 * @param {String} rule_id - rule id of table element
 *
 * @return {RuleResult | null}}  Returns rule result object if rule id is found, otherwise null
 */
 
 OpenAjax.a11y.cache.TablesCache.prototype.getRuleResultByRuleId = function (rule_id) {

  var i;
  var rr;
  var rule_results_len = this.rule_results.length;
   
  for (i = 0; i < rule_results_len; i++) {
    rr = this.rule_results[i];

    if (rr.rule.rule_id == rule_id) {
      return rr;
    }
  }
     
  return null;
};


/**
 * @method updateCacheItems
 *
 * @memberOf OpenAjax.a11y.cache.TablesCache
 *
 * @desc Updates the tables cache object by checking to see if a dom element object
 *          should be added to the table cache objects
 *  
 * @param  {DOMElement}  dom_element  - DOMElement object to check for inclusion in tables cache
 * @param  {TableInfo}   table_info   - Information about the current table relationships in the DOM
 */
 
 OpenAjax.a11y.cache.TablesCache.prototype.updateCacheItems = function (dom_element, table_info) {

   var te;
   var tce;
   var ce;
   var tbe;
   var the;
   var tre;
   
   var ti = new OpenAjax.a11y.cache.TableInfo(table_info);

   switch (dom_element.tag_name) {

     case 'table':
       te = new OpenAjax.a11y.cache.TableElement(this.dom_cache, dom_element, table_info);
       this.addTableElement(te);
  
       if (table_info.parent_element) {
         table_info.parent_element.addChild(te);   
       }
       else {
         this.addChild(te);
       }
       
       ti.parent_element = te;   
       ti.table_element  = te;    
       ti.table_body_element  = null;    
       ti.table_row_element  = null;    
       
       this.dom_cache.getNameForTable(te);
             
       break;

     case 'caption':
       ce = new OpenAjax.a11y.cache.CaptionElement(dom_element, table_info);
       
       te = table_info.table_element;

       if (te) {
         table_info.table_element.addTableElement(ce);   
         if (table_info.parent_element) {
           table_info.parent_element.addChild(ce);
         }
         
         if (te.accessible_name_source === OpenAjax.a11y.SOURCE.NONE) {
           this.dom_cache.getNameForTable(te, ce.name);
         }
         
       }
 
       break;

     case 'thead':
       the = new OpenAjax.a11y.cache.THeadElement(dom_element, table_info);

       if (table_info.table_element) {
         table_info.table_element.addTableElement(the);   
         if (table_info.parent_element) {
           table_info.parent_element.addChild(the);   
         } 
       }

       ti.parent_element     = the;   
       ti.table_body_element = the;   
       ti.table_row_element  = null;    

       break;

     case 'tbody':
       tbe = new OpenAjax.a11y.cache.TBodyElement(dom_element, table_info);

       if (table_info.table_element) {
         table_info.table_element.addTableElement(tbe);   
         
         if (table_info.parent_element) {
           table_info.parent_element.addChild(tbe);   
         }
       }
       
       ti.parent_element = tbe;   
       ti.table_body_element = tbe;   
       ti.table_row_element  = null;    

       break;

     case 'tr':
       tre = new OpenAjax.a11y.cache.TableRowElement(dom_element, table_info);

       if (table_info.table_element) {
         table_info.table_element.addTableElement(tre);   
         
         if (table_info.parent_element) {
           table_info.parent_element.addChild(tre);   
         }
         
         if (table_info.table_body_element) {
           table_info.table_body_element.row_count++;
         }
       }
 
       ti.parent_element     = tre;
       ti.table_row_element  = tre;

       break;


     case 'td':
     case 'th':
       tce = new OpenAjax.a11y.cache.TableCellElement(dom_element, table_info);
       
       if (table_info.table_element) {
         table_info.table_element.addTableElement(tce);   
                  
         if (table_info.parent_element) {
           table_info.parent_element.addChild(tce);   
         }
       }
 
       ti.parent_element      = tce;
       
       break;
       
     case 'body':
       if (!this.page_element) {  
         this.page_element = new OpenAjax.a11y.cache.PageElementLayout(dom_element);
       } 
       break;
           
     default:
       break;

   } // end switch

   return ti;
 };

/**
 * @method traverseDOMElementsForTableElements
 *
 * @memberOf OpenAjax.a11y.cache.TablesCache
 *
 * @desc Traverses the DOMElements to update table elements
 *
 * @param {TableElement}      dom_element  - DOMElement object to check fo inclusion in tables cache
 * @param {TableInformation}  table_info   - Information needed for identifying the parent/child relationships of nested tables
 */
 
 OpenAjax.a11y.cache.TablesCache.prototype.traverseDOMElementsForTableElements = function (dom_element, table_info) {

   var i;
   var ti;

   if (!dom_element) return;

     if (dom_element.type == Node.ELEMENT_NODE) {

       ti = this.updateCacheItems(dom_element, table_info);
  
       for (i=0; i<dom_element.child_dom_elements.length; i++ ) {
         this.traverseDOMElementsForTableElements(dom_element.child_dom_elements[i], ti);
       } // end loop
     }  
 }; 

/**
 * @method updateCache
 *
 * @memberOf OpenAjax.a11y.cache.TablesCache
 *
 * @desc Traverses the DOMElements to update the tables cache
 *       NOTE: This function is only used when the specialized caches
 *       are build as rules need them.  In this condition, if the rules 
 *       dependent on the controls cache are disabled, this cache would 
 *       not be updated
 */
 
 OpenAjax.a11y.cache.TablesCache.prototype.updateCache = function () {
   
   var i;
   var children = this.dom_cache.element_cache.child_dom_elements;
   var children_len = children.length;

   var table_info = new OpenAjax.a11y.cache.TableInfo(null);

 
   for (i=0; i < children_len; i++) {
     this.traverseDOMElementsForTableElements(children[i], table_info);
   }  
   
   this.up_to_date = true;
 };

/* ---------------------------------------------------------------- */
/*                       TableElement Object                        */
/* ---------------------------------------------------------------- */

/**
 * @constructs TableElement
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates table element object used to hold data about a table 
 *         
 * @param  {DOMCache} dom_cache - Reference to the current dom cache for use of 
 *                                dom cache methods to find references   
 *
 * @param  {DOMElement}        dom_element         - dom_element object provides information about current dom node 
 * @param  {TableCellElement}  table_cell_element  - table_cell_element object provides information about the table 
 *                                                   cell the table may be a child of in the dom 
 *
 * @property  {DOMCache}    dom_cache    - DOMCache reference for reference DOMCache methods for calculating headers
 * @property  {DOMElement}  dom_element  - DOMElement associated with the form element
 * @property  {String}      cache_id     - String that uniquely identifies the cache element object in the cache
 * @property  {Number}      document_order  - Ordinal position of the table element in the document in relationship to other table elements
 *
 * @property  {Array}       child_cache_elements  - Array of cache table elements as part of table elements relationship tree 
 * @property  {Array}       table_elements        - List of all table element objects in this table element object
 * @property  {Number}      length                - Number of table element objects 
 *
 * @property  {String}  accessible_name                - The accessible name for the table
 * @property  {String}  accessible_name_for_comparison - The accessible name for the table used for comparison
 * @property  {Number}  accessible_name_length         - Length of the accessible name used for comparison
 * @property  {Number}  accessible_name_source         - Numeric constant representing the source of the accessible name
 *                                                       - aria-labelledby
 *                                                       - aria-label
 *                                                       - summary attribute
 *                                                       - caption element
 *                                                       - title
 *
 * @property  {String}  accessible_description                 - Accessible description for the table (from aria-describedby)
 * @property  {String}  accessible_description_for_comparison  - Accessible description for the table used for comparison
 
 * @property  {Number}  max_row     - Number of rows in a table  
 * @property  {Number}  max_column  - Number of columns in a table
 * @property  {Number}  cell_count  - Number of cells (i.e. th and td elements) in a table
 * 
 * @property  {Number}  row     - Used as the current row counter when traversing a table dom elements  
 * @property  {Number}  column  - Used as the current column counter when traversing a table dom elements
 * 
 * @property  {Array}   cells     - A two dimensional array representing the table row and columns
 * @property  {Array}   cell_ids  - List of table cell objects who have an id attribute defined
 *
 * @property  {Number}   table_role             - Constant identifying the role of the table: layout, data or unknown role
 * @property  {Boolean}  is_complex_data_table  - True if the table is identified as a complex data table
 *
 * @return {TableElement}
 */
 OpenAjax.a11y.cache.TableElement = function (dom_cache, dom_element, table_info) {

   if( !dom_element ) return null;  

   this.type = OpenAjax.a11y.TABLE.TABLE_ELEMENT;

   this.dom_cache      = dom_cache;
   this.dom_element    = dom_element;
   this.cache_id       = "";
   this.document_order = 0;
   
   this.child_cache_elements = [];

   this.table_elements = [];
   this.length = 0;

   this.max_row = 0; 
   this.max_column = 0;
   this.cell_count = 0;

   this.cell_count = 0;
   
   this.cell_ids = [];

   this.row      = -1;   
   this.column   = 0;  
   
   this.cells    = [];
   this.cells[0]  = [];
   this.cells[0][0] = null;
 
   if (dom_element.role && 
       (dom_element.role === 'presentation')) this.table_role = OpenAjax.a11y.TABLE_ROLE.LAYOUT;
   else this.table_role = OpenAjax.a11y.TABLE_ROLE.UNKNOWN;
   
   this.is_complex_data_table = false;

   this.nesting_level        = 0;
   this.layout_nesting_level = 0;
   this.layout_table_in_data_table = false;
   this.data_table_in_data_table = false;

   this.parent_table_element = table_info.table_element;
   
   if (table_info.table_element) {
     
     this.nesting_level = table_info.table_element.nesting_level + 1;
     
     if (table_info.table_element.table_role === OpenAjax.a11y.TABLE_ROLE.DATA) {
       this.layout_in_data_table = true; 
     }
     else {
       this.layout_nesting_level = table_info.table_element.layout_nesting_level + 1;   
     }
     
   }
 
   return this;
 };

/**
 * @method setIsDataTable
 *
 * @memberOf OpenAjax.a11y.cache.TableElement
 *
 * @desc  Set is a data table property, if not already set   
 */
 
OpenAjax.a11y.cache.TableElement.prototype.setIsDataTable = function () {

  // if role=presentation this is a layout table
  if (this.dom_element.has_role  &&
      (this.dom_element.role === 'presentation')) {
    
    this.setIsLayoutTable();
    return; 
  }  

  if(this.table_role === OpenAjax.a11y.TABLE_ROLE.DATA) return;

  this.table_role = OpenAjax.a11y.TABLE_ROLE.DATA;

  if (this.parent_table_element) {
     
    if (this.parent_table_element.table_role === OpenAjax.a11y.TABLE_ROLE.DATA) {
      this.layout_table_in_data_table = false; 
      this.data_table_in_data_table = true; 
    }
     
    this.layout_nesting_level = this.parent_table_element.layout_nesting_level + 1;   
     
  }
  
};

/**
 * @method setIsLayoutTable
 *
 * @memberOf OpenAjax.a11y.cache.TableElement
 *
 * @desc  Set is a data table property, if not already set   
 */
 
OpenAjax.a11y.cache.TableElement.prototype.setIsLayoutTable = function () {

  this.table_role = OpenAjax.a11y.TABLE_ROLE.LAYOUT;

  if (this.parent_table_element) {
     
    if (this.parent_table_element.table_role === OpenAjax.a11y.TABLE_ROLE.DATA) {
      this.layout_table_in_data_table = true; 
      this.data_table_in_data_table = false; 
    }
     
    this.layout_nesting_level = this.parent_table_element.layout_nesting_level + 1;   
     
  }
  
};


/**
 * @method getTableElementByCacheId 
 *
 * @memberOf OpenAjax.a11y.cache.TableElement
 *
 * @desc  Retrieve table cache element from the tree of table cache elements
 *
 * @param  {String}  cache_id  -  cache_id of a table cache element 
 *
 * @return  {CaptionElement | TheadElement | TBodyElement | TableRowElement | TableCellElement | null}  Returns table cache element if cahce id is found, otherwise null
 */
OpenAjax.a11y.cache.TableElement.prototype.getTableElementByCacheId = function (cache_id) {
  return this.getItemByCacheId(cache_id);
};
 
/**
 * @method getItemByCacheId 
 *
 * @memberOf OpenAjax.a11y.cache.TableElement
 *
 * @desc  Retrieve table cache element from the tree of table cache elements
 *
 * @param  {String}  cache_id  -  cache_id of a table cache element 
 *
 * @return  {CaptionElement | TheadElement | TBodyElement | TableRowElement | TableCellElement | null}  Returns table cache element if cahce id is found, otherwise null
 */
 
OpenAjax.a11y.cache.TableElement.prototype.getItemByCacheId = function (cache_id) {

   function traverseTableElements(table_elements) {
     var table_elements_len = table_elements.length;
     var to;
     var i;
     var ro;
     
     for (i = 0; i < table_elements_len; i++) {
       to = table_elements[i];
       
       if (to.cache_id == cache_id) {
         return to;
       }
       else {
         if (to.child_cache_elements && to.child_cache_elements.length) {
           ro = traverseTableElements(to.child_cache_elements);
           if (ro) return ro;
         }
       }
     } // end loop
   
     return null;
   }

   return traverseTableElements(this.child_cache_elements);
 
 };

/**
 * @method addTableElement 
 *
 * @memberOf OpenAjax.a11y.cache.TableElement
 *
 * @desc    Adds a table cache element object to table_elements array and generates a cache id value for the table element object
 *
 * @param  {CaptionElement | TableTHeadElement | TableTBodyElement | TableRowElement | TableCellElement} table_element  - table cache element to add
 *
 * @return  {Number}  Returns the length the list of table elements 
 *
 */
 OpenAjax.a11y.cache.TableElement.prototype.addTableElement = function (table_element) {

   var caption;
   var summary;

   this.length = this.length + 1;
   table_element.document_order = this.length;
   table_element.cache_id = this.cache_id + "_te_" + this.length;
     
   this.table_elements.push(table_element);

   switch (table_element.table_type) {
   
   case OpenAjax.a11y.TABLE.CAPTION_ELEMENT:
     this.setIsDataTable();
     this.effective_caption                = table_element.name;
     this.effective_caption_for_comparison = table_element.name_for_comparision;
     break;
   
   case OpenAjax.a11y.TABLE.THEAD_ELEMENT:
     this.setIsDataTable();
     break;

   case OpenAjax.a11y.TABLE.TBODY_ELEMENT:
     break;

   case OpenAjax.a11y.TABLE.TR_ELEMENT:
     this.nextRow();   
     break;

   case OpenAjax.a11y.TABLE.TH_ELEMENT:
     this.setIsDataTable();

     if ((table_element.number_of_header_ids > 1) ||
         (table_element.row_span             > 1) ||
         (table_element.column_span          > 1)) {
       this.is_complex_data_table = true;        
     }

     this.addTableCellElement(table_element);
     break;   

   case OpenAjax.a11y.TABLE.TD_ELEMENT:  

//   OpenAjax.a11y.logger.debug("  Data Table Assumption: " + OpenAjax.a11y.DATA_TABLE_ASSUMPTION);

     if (this.table_role === OpenAjax.a11y.TABLE_ROLE.DATA) {
       if ((table_element.number_of_header_ids > 1) ||
           (table_element.row_span             > 1) ||
           (table_element.column_span          > 1)) {
         this.setIsDataTable();
         this.is_complex_data_table = true;        
       }     
     } 
     
     this.addTableCellElement(table_element);
     break;
     
   default:
     break;


   } // end switch

   return this.length;

 };

/**
 * @method addChild 
 *
 * @memberOf OpenAjax.a11y.cache.TableElement
 * 
 * @desc Adds a cache table element to the root tree representation of the tree cache
 *
 * @param  {TableElement | CaptionElement | THeadElement | TBodyElement | TableRowElement | TableCellElement }  table_element  - Cache table element object to add to root of tree of table elements 
 */

OpenAjax.a11y.cache.TableElement.prototype.addChild = function (table_element) {

 if (table_element) {
  this.child_cache_elements.push(table_element); 
 }  

}; 

/**
 * @method nextRow
 *
 * @memberOf OpenAjax.a11y.cache.TableElement
 *
 * @desc Updates the current table cell counters and array to start a new row in the table
 */
 OpenAjax.a11y.cache.TableElement.prototype.nextRow = function () {
 
   this.row = this.row + 1;
   this.max_row = this.row+1; // 1 based index
      
   // see if there is already a row created
   if (typeof(this.cells[this.row]) != 'object') {
     // If row does not exist create it
     this.cells[this.row] = [];
     this.cells[this.row][0] = null;
   }
   
   if (!this.is_complex_data_table && this.max_column > 2 ) {
	 this.multipleTHInRow(this.max_row-1); 	   
   } 
   
   if (!this.is_complex_data_table && this.max_row > 2) {
	 this.multipleTHInColumn(); 	   
   }
 };
 
/**
 * @method multipleTHInRow
 *
 * @memberOf OpenAjax.a11y.cache.TableElement
 * 
 * @desc Tests to see if there are multiple table header cells in a row and sets complex data table flag if there are
 * 
 * @param {Number} row - Number of the row to test for headers
 */
 OpenAjax.a11y.cache.TableElement.prototype.multipleTHInRow = function(row) {
   
   if (!this.cells[row] || this.cells[row].length < 2) return;
   
   var i;
   var th_count = 0;
   var td_count = 0;
   
   var row_len = this.cells[row].length;
   var cell;
   
   for (i=0; i<row_len; i++) {
   
	 cell = this.cells[row][i];
	
	 if (cell && 
	     cell.table_type == OpenAjax.a11y.TABLE.TH_ELEMENT) {
	   th_count++;
	 }
	 else {
	   td_count++;
	 }
	 
   }   

   if (th_count > 1 && td_count > 0) {
     this.is_complex_data_table = true;
   } 


 };

/**
 * @method multipleTHInColumn
 *
 * @memberOf OpenAjax.a11y.cache.TableElement
 * 
 * @desc Tests to see if there are multiple table header cells in a column and sets complex data table flag if there are
 * 
 * @param {Number} column - Number of the column to test for headers
 */
 OpenAjax.a11y.cache.TableElement.prototype.multipleTHInColumn = function() {
      
   var th_count;
   var td_count;

   var row_max = this.max_row;
   var col_max = this.cells[0].length;
   var row_len;
   var cell;
   
   for (var c = 0; c < col_max; c++) {   
     th_count = 0;
     td_count = 0;
   
     for (var r=0; r < row_max; r++) {
	   cell = this.cells[r][c];
	
  	   if (cell && 
  	       cell.table_type == OpenAjax.a11y.TABLE.TH_ELEMENT) {
	     th_count++;
	   }
	   else {
	     td_count++;
	   }

       if (th_count > 1 && td_count > 1) {
         this.is_complex_data_table = true;
         return;
       }  
	 }
	 
   }   
 };

/**
 * @method addTableCellElement
 *
 * @memberOf OpenAjax.a11y.cache.TableElement
 *
 * @desc Adds a TableCellElement to the current row
 *
 * @param {TableCellElement}  table_cell_element  -  The table cell element object to add in to the current row and column of a table 
 */
 OpenAjax.a11y.cache.TableElement.prototype.addTableCellElement = function (table_cell_element) {

   var i;
   var j;
   var r;
   var c;
   
   this.column = 0; 
  
   if (table_cell_element.id && 
       table_cell_element.id.length) {
     this.cell_ids.push(table_cell_element.id);
   }

   // find the next available spot in cells array, this needs to be calculated due to row and column spanning 
   while ((this.cells[this.row][this.column] !== undefined) &&
          (this.cells[this.row][this.column] !== null)) {
     this.column++;
   } // end loop

   r = this.row;
   c = this.column;
     
   table_cell_element.row    = r;
   table_cell_element.column = c;

   for (i=0; i<table_cell_element.row_span; i++) {
     
     for (j=0; j<table_cell_element.column_span; j++) {
       this.cells[r][c] = table_cell_element;
       c += 1;
     }
     r += 1;
  
     // see if there is already a row created
     if (typeof(this.cells[r]) != 'object') {
       // If row does not exist create it
       this.cells[r] = [];
       this.cells[r][0] = null;
     } 
   }  
   this.setTableCellHeader(this.row, this.column, table_cell_element);

   if (c > this.max_column) this.max_column = c; 
   
   this.cell_count++;
   
 }; 

/**
 * @method sortCellIds
 *
 * @memberOf OpenAjax.a11y.cache.TableElement
 * 
 * @desc Sorts the cell ids array for this table based on the id values
 */
 OpenAjax.a11y.cache.TableElement.prototype.sortCellIds = function () {

   var swapped = false;
   var temp = null;
   var i;
 
   var cell_ids_len = this.cell_ids.length;

   do{
     swapped = false;
     
     for (i = 1; i < cell_ids_len; i++ ) {
     
       if (this.cell_ids[i-1] > this.cell_ids[i]) {
         
         // swap the values
         temp = this.cell_ids[i-1];
         this.cell_ids[i-1] = this.cell_ids[i];
         this.cell_ids[i] = temp;
         swapped = true;
       } 
     } // end loop
   } while (swapped);
 };

/**
 * @method setTableCellHeader
 *
 * @memberOf OpenAjax.a11y.cache.TableElement
 *
 * @desc Sets header content property of the table cell element object 
 *
 * @param {Number}            row                 - Current row position of the table cell element object
 * @param {Number}            column              - Current column position of the table cell element object
 * @param {TableCellElement}  table_cell_element  - Table cell element object to create header content property
 */
 OpenAjax.a11y.cache.TableElement.prototype.setTableCellHeader = function (row, column, table_cell_element) {

   var tag_name;   
   var scope;
   var string_array = [];
   var cell, r, c;

   tag_name = table_cell_element.dom_element.tag_name;
   scope  = table_cell_element.dom_element.scope;
   
   table_cell_element.header_source =  OpenAjax.a11y.HEADER_SOURCE.NONE;
 
   if (table_cell_element.headers) {
     var hc = this.dom_cache.element_with_id_cache.getTextFromIds(table_cell_element.headers);
     table_cell_element.header_content = hc.normalizeSpace();
     if (table_cell_element.header_content.length) table_cell_element.header_source =  OpenAjax.a11y.HEADER_SOURCE.HEADERS_ATTRIBUTE;
   }
   else {
     // if a table cell is used as a header in the table and has no header attribute set its header to an empty string
     if (table_cell_element.table_type === OpenAjax.a11y.TABLE.TH_ELEMENT) {
       table_cell_element.header_content = "";
     }   
     else {

       // find TH or TD with scope=column in the same column
       for (r=(row-1); r>=0; r--) {
         cell = this.cells[r][column];
    
         if (cell) {
           tag_name = cell.dom_element.tag_name;
           scope  = cell.scope;
      
           if (tag_name == "th" || scope == "col") {
             if (!cell.cell_text) cell.cell_text = cell.dom_element.getText().normalizeSpace(); 
             string_array.push(cell.cell_text);
           }
         }     
       } 

       // find TH or TD with scope=row in the same row
       for (c=(column-1); c>=0; c--) {
         cell = this.cells[row][c];
    
         if (cell) {
           tag_name = cell.dom_element.tag_name;
           scope  = cell.scope;
          
           if (tag_name == "th" || scope == "row") {
             if (!cell.cell_text) cell.cell_text = cell.dom_element.getText().normalizeSpace(); 
             string_array.push(cell.cell_text);
           }
         } 
       } 
       table_cell_element.header_content = string_array.join(' ').normalizeSpace();
       
       if (table_cell_element.header_content.length) table_cell_element.header_source =  OpenAjax.a11y.HEADER_SOURCE.ROW_OR_COLUMN_HEADERS;
     } 
   } 
 };

/**
 * @method findFirstRowWithContent
 *
 * @memberOf OpenAjax.a11y.cache.TableElement
 *
 * @desc Finds the first row of the table which has text content in at least one cell.
 *       This is used to skip rows that are used for stylistic puposes, since they usually
 *       do not have any text content other than spaces in them.
 *
 * @return {Number}  Returns number of first row with content  
 * 
 */
 OpenAjax.a11y.cache.TableElement.prototype.findFirstRowWithContent = function() {
 
   var r;
   var c;
   var max_row = this.max_row;
   var max_col;
   var text;
   var cell;
 
   for (r = 0; r < max_row; r++) {
     max_col = this.cells[r].length;
 
     for (c = 0; c < max_col; c++) {
       cell = this.cells[r][c];
       
       if (!cell || !cell.dom_element) continue;
       
       text = cell.dom_element.getText();
    
       if (text) text = text.normalizeSpace();
       
       if (cell.table_type == OpenAjax.a11y.TABLE.TH_ELEMENT || 
           text.length) {
         return r;
       }
     }
   }
   return -1;
 };

/**
 * @method findFirstColumnWithContent
 *
 * @memberOf OpenAjax.a11y.cache.TableElement
 *
 * @desc Finds the first column of the table which has text content in at least one cell.
 *       This is used to skip columns that are used for stylistic puposes, since they usually
 *       do not have any text content other than spaces in them.
 *
 * @return {Number}  Returns number of first column with content  
 * 
 */
 OpenAjax.a11y.cache.TableElement.prototype.findFirstColumnWithContent = function() {
 
   var r;
   var c;
   var max_col = this.max_column;
   var max_row = this.max_row;
   var text;
   var cell;
 
   for (c = 0; c < max_col; c++) {
 
     for (r = 0; r < max_row; r++) {
     
       cell = this.cells[r][c];
       
       if (!cell || !cell.dom_element) continue;
       
       text = cell.dom_element.getText();
       
       if (text) text = text.normalizeSpace();
       
       if (cell.table_type == OpenAjax.a11y.TABLE.TH_ELEMENT || 
           text.length) {
         return c;
       }
     }
   }
   return -1;
 };


/**
 * @method headerCellsInFirstRow
 *
 * @memberOf OpenAjax.a11y.cache.TableElement
 *
 * @desc Calculates the number of non-empty cells in the first row with content and
 *       how many of the non-empty cells are header cells
 *
 * @return {Object} Returns an object with two properties 'total' and 'th_count'  
 */
 OpenAjax.a11y.cache.TableElement.prototype.headerCellsInFirstRow = function () {

   // ro is the Return Object 
   var ro = {};
   ro.total = 0;
   ro.th_count = 0;

   var c;
   var max_col;
   var cell;
   var text;
 
   var r = this.findFirstRowWithContent();
   
   if (r < 0) return ro;
   
   if (this.cells[r]) {
   
     max_col = this.cells[r].length;
 
     for (c = 0; c < max_col;) {
       cell = this.cells[r][c];
     
       if (cell.table_type == OpenAjax.a11y.TABLE.TH_ELEMENT) {
         ro.total++;   
         ro.th_count++;   
       }
       else {
         text = cell.dom_element.getText();
   
         if (text) text = text.normalizeSpace();
   
         if (text.length) {
           ro.total++;
         } 
       }
       c += cell.column_span;
     }
   }  
   return ro;
 };

/** 
 * @method headerCellsInFirstColumn
 *
 * @memberOf OpenAjax.a11y.cache.TableElement
 *
 * @desc Calculates the number of non-empty cells in the first column with content and
 *       how many of the non-empty cells are header cells
 *
 * @return {Object} Returns an object with two properties 'total' and 'th_count'  
 */
 OpenAjax.a11y.cache.TableElement.prototype.headerCellsInFirstColumn = function () {

   // ro is the Return Object 
   var ro = {};
   ro.total = 0;
   ro.th_count = 0;
 
   var r;
   var c;
   var text;
   var cell;
   var max_row;
 
   c = this.findFirstColumnWithContent();
   
   if (c < 0) return ro; 
   
   max_row = this.max_row;
 
   for (r = 0; r < max_row;) {
     cell = this.cells[r][c];
     
     if (!cell) break;
     
     if (cell.table_type == OpenAjax.a11y.TABLE.TH_ELEMENT) {
       ro.total++;   
       ro.th_count++;   
     }
     else {
       text = cell.dom_element.getText();
   
       if (text) text = text.normalizeSpace();
   
       if (text.length) {
         ro.total++;
       } 
     }
     r += cell.row_span;
   } 
   return ro;
 };

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.TableElement
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.TableElement.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.TableElement
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.TableElement.prototype.getAttributes = function (unsorted) {
   
  var cache_nls = OpenAjax.a11y.cache_nls;
  
  var attributes = this.dom_element.getAttributes(unsorted);
  
  attributes.push(cache_nls.getLabelAndValueNLS('summary', this.summary));
  attributes.push(cache_nls.getLabelAndValueNLS('role', this.dom_element.role));
  
  if (!unsorted) this.dom_element.sortItems(attributes);
  
  return attributes;
};

/**
 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.TableElement
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event information
 */

OpenAjax.a11y.cache.TableElement.prototype.getEvents = function (unsorted) {
   
  return this.dom_element.getEvents();
  
};


/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.TableElement
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style items
 */

OpenAjax.a11y.cache.TableElement.prototype.getStyle = function () {

  return this.dom_element.getStyle();
  
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.TableElement
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @return {Array} Returns a array of cache properties
 */

OpenAjax.a11y.cache.TableElement.prototype.getCacheProperties = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  var properties = this.dom_element.getCacheProperties();
  
  properties.push(cache_nls.getLabelAndValueNLS('table_role', this.table_role));
  properties.push(cache_nls.getLabelAndValueNLS('is_complex_data_table', this.is_complex_data_table));
  properties.push(cache_nls.getLabelAndValueNLS('accessible_name', this.accessible_name));
  properties.push(cache_nls.getLabelAndValueNLS('accessible_name_for_comparison', this.accessible_name_for_comparison));
  properties.push(cache_nls.getLabelAndValueNLS('accessible_name_source', this.accessible_name_source));
  properties.push(cache_nls.getLabelAndValueNLS('max_row', this.max_row));
  properties.push(cache_nls.getLabelAndValueNLS('max_column', this.max_column));
  properties.push(cache_nls.getLabelAndValueNLS('cell_count', this.cell_count));
  properties.push(cache_nls.getLabelAndValueNLS('nesting_level', this.nesting_level));
  
  this.dom_element.sortItems(properties);
  
  return properties;
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.TableElement
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.TableElement.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};


/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.TableElement
 *
 * @desc Creates a text string representation of the table element object 
 *
 * @return {String} Returns a text string representation of the table
 */
 OpenAjax.a11y.cache.TableElement.prototype.toString = function () {
   var str = this.max_column + "x" + this.max_row + " ";
   
   if (this.table_role === OpenAjax.a11y.TABLE_ROLE.DATA) {
     
     str += "Data Table: ";
     
     if (this.accessible_name_length) str += this.accessible_name;
     else  str += "no name";
     
   } 
   else {
     if (this.table_role === OpenAjax.a11y.TABLE_ROLE.LAYOUT) str += "Layout Table ";   
     else str += "Table: unknown role on page";
   }
   
   return str;
     
 };


/* ---------------------------------------------------------------- */
/*                         CaptionElement Object                    */
/* ---------------------------------------------------------------- */

/**
 * @constructor CaptionElement
 * 
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates a caption element object which contains 
 *       information obout a caption element in a table
 *          
 * @param  {DOMElement}  dom_element  - Reference to the dom element object associated with caption element 
 * @param  {TableInfo}   table_info   - Information about the current table relationships in the DOM
 * 
 * @property  {DOMElement}  dom_element  - Reference to the dom element object associated with caption element 
 * @property  {String}      cache_id     - String that uniquely identifies the cache element object in the cache
 *
 * @property  {TableElement}  parent_table_element  - Reference to the table element object that contatins the caption element
 *
 * @property  {Number}  type                 - Constant indicating the type of table cache element object
 *
 * @property  {String}  name                 - The text content of the caption element
 * @property  {String}  name_for_comparison  - The text content used for comparisons with other text content (i.e. lowercase, space normalized and trimmed)
 */
 
OpenAjax.a11y.cache.CaptionElement = function (dom_element, table_info) {

  this.dom_element = dom_element;

  var name  = dom_element.getText(); 
  this.name = name;
  this.name_for_comparison = name.normalizeSpace();
  
  this.table_type = OpenAjax.a11y.TABLE.CAPTION_ELEMENT;

  this.parent_table_element = table_info.table_element;
    
};

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.CaptionElement
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.CaptionElement.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.CaptionElement
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.CaptionElement.prototype.getAttributes = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  var attributes = this.dom_element.getAttributes();
  
  return attributes;
};

/**
 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.CaptionElement
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event information
 */

OpenAjax.a11y.cache.CaptionElement.prototype.getEvents = function (unsorted) {
   
  return this.dom_element.getEvents();
  
};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.CaptionElement
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style items
 */

OpenAjax.a11y.cache.CaptionElement.prototype.getStyle = function () {

  return this.dom_element.getStyle();
  
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.CaptionElement
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @return {Array} Returns a array of cache properties
 */

OpenAjax.a11y.cache.CaptionElement.prototype.getCacheProperties = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  var properties = this.dom_element.getCacheProperties();
  
  return properties;
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.CaptionElement
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.CaptionElement.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};


/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.CaptionElement
 *
 * @desc Creates a text string representation of the caption element object 
 *
 * @return {String} Returns a text string representation of the caption element object
 */
 
 OpenAjax.a11y.cache.CaptionElement.prototype.toString = function () {
   return "caption: " + this.name;   
 };

/* ---------------------------------------------------------------- */
/*                         THeadElement Object                      */
/* ---------------------------------------------------------------- */

/**
 * @constructor THeadElement
 * 
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates a thead element object which contains 
 *       information obout a thead element in a table
 *          
 * @param  {DOMElement}  dom_element  - Reference to the dom element object associated with thead element 
 * @param  {TableInfo}   table_info   - Information about the current table relationships in the DOM
 * 
 * @property  {DOMElement}  dom_element  - Reference to the dom element object associated with thead element 
 * @property  {String}      cache_id     - String that uniquely identifies the cache element object in the cache
 *
 * @property  {Array}         child_cache_elements  - Array of table cache elements for the tree representation of the table
 * @property  {TableElement}  parent_table_element  - Reference to the table element object that contatins the thead element
 *
 * @property  {Number}  type       - Constant indicating the type of table cache element object
 *
 * @property  {Number}  row_count  - Number of table rows contained in the childresn of the thead element
 */
 
OpenAjax.a11y.cache.THeadElement = function (dom_element, table_info) {

  this.dom_element = dom_element;
  this.cache_id    = "";
  
  this.child_cache_elements = [];
  this.parent_table_element = table_info.table_element;

  this.table_type = OpenAjax.a11y.TABLE.THEAD_ELEMENT;
  
  this.row_count = 0;

};

/**
 * @method addChild
 *
 * @memberOf OpenAjax.a11y.cache.THeadElement
 * 
 * @desc Adds a cache table element to the tree representation of the table in the cache
 *
 * @param  {TableElement | CaptionElement | THeadElement | TBodyElement | TableRowElement | TableCellElement }  table_element  - Cache table element object to add to root of tree of table elements 
 */

OpenAjax.a11y.cache.THeadElement.prototype.addChild = function (child_object) {

  if (child_object) {
    this.child_cache_elements.push(child_object); 
  }  

}; 

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.THeadElement
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.THeadElement.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.THeadElement
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.THeadElement.prototype.getAttributes = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  var attributes = this.dom_element.getAttributes();
  
  attributes.push(cache_nls.getLabelAndValueNLS('role', this.dom_element.role));
  
  return attributes;
};



/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.THeadElement
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style items
 */

OpenAjax.a11y.cache.THeadElement.prototype.getStyle = function () {

  return this.dom_element.getStyle();
  
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.THeadElement
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @return {Array} Returns a array of cache properties
 */

OpenAjax.a11y.cache.THeadElement.prototype.getCacheProperties = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  var properties = this.dom_element.getCacheProperties();
  
  return properties;
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.THeadElement
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.THeadElement.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};


/**
 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.THeadElement
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event information
 */

OpenAjax.a11y.cache.THeadElement.prototype.getEvents = function (unsorted) {
   
  return this.dom_element.getEvents();
  
};

/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.THeadElement
 *
 * @desc Creates a text string representation of the thead element object 
 *
 * @return {String} Returns a text string representation of the thead element object
 */
 OpenAjax.a11y.cache.THeadElement.prototype.toString = function () {
   var str = "thead: " + this.row_count + " rows";   
   
   if (this.row_count === 1 ) str =  "thead: " + this.row_count + " row";
   
   return str;
 };


/* ---------------------------------------------------------------- */
/*                         TBodyElement Object                      */
/* ---------------------------------------------------------------- */

/**
 * @constructor TBodyElement
 * 
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates a tbody element object which contains 
 *       information obout a tbody element in a table
 *          
 * @param  {DOMElement}  dom_element  - Reference to the dom element object associated with tbody element 
 * @param  {TableInfo}   table_info   - Information about the current table relationships in the DOM
 * 
 * @property  {DOMElement}  dom_element  - Reference to the dom element object associated with tbody element 
 * @property  {String}      cache_id     - String that uniquely identifies the cache element object in the cache
 *
 * @property  {Array}         child_cache_elements  - Array of table cache elements for the tree representation of the table
 * @property  {TableElement}  parent_table_element  - Reference to the table element object that contatins the tbody element
 *
 * @property  {Number}  type                 - Constant indicating the type of table cache element object
 *
 * @property  {Number}  row_count            - Number of table rows contained in the childresn of the tbody element
 */
 
OpenAjax.a11y.cache.TBodyElement = function (dom_element, table_info) {

  this.dom_element          = dom_element;
  this.child_cache_elements = [];
  this.parent_table_element = table_info.table_element;

  this.table_type = OpenAjax.a11y.TABLE.TBODY_ELEMENT;

  this.row_count = 0;

};

/**
 * @method addChild
 *
 * @memberOf OpenAjax.a11y.cache.TBodyElement
 * 
 * @desc Adds a cache table element to the tree representation of the table in the cache
 *
 * @param  {TableElement | CaptionElement | THeadElement | TBodyElement | TableRowElement | TableCellElement }  table_element  - Cache table element object to add to root of tree of table elements 
 */

OpenAjax.a11y.cache.TBodyElement.prototype.addChild = function (child_object) {

 if (child_object) {
  this.child_cache_elements.push(child_object); 
 }  

}; 

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.TBodyElement
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.TBodyElement.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.TBodyElement
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.TBodyElement.prototype.getAttributes = function () {
  var cache_nls = OpenAjax.a11y.cache_nls;
  
  var attributes = this.dom_element.getAttributes();
  
  attributes.push(cache_nls.getLabelAndValueNLS('role', this.dom_element.role));
  
  return attributes;
};


/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.TBodyElement
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style items
 */

OpenAjax.a11y.cache.TBodyElement.prototype.getStyle = function () {

  return this.dom_element.getStyle();
  
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.TBodyElement
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @return {Array} Returns a array of cache properties
 */

OpenAjax.a11y.cache.TBodyElement.prototype.getCacheProperties = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  var properties = this.dom_element.getCacheProperties();
  
  return properties;
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.TBodyElement
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.TBodyElement.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};


/**
 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.TBodyElement
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event information
 */

OpenAjax.a11y.cache.TBodyElement.prototype.getEvents = function (unsorted) {
   
  return this.dom_element.getEvents();
  
};

/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.TBodyElement
 *
 * @desc Creates a text string representation of the tbody element object 
 *
 * @return {String} Returns a text string representation of the tbody element object
 */
 OpenAjax.a11y.cache.TBodyElement.prototype.toString = function () {
   var str = "tbody: " + this.row_count + " rows";   
   
   if (this.row_count === 1 ) str =  "tbody: " + this.row_count + " row";
   
   return str;
 };


/* ---------------------------------------------------------------- */
/*                       TableRowElement Object                     */
/* ---------------------------------------------------------------- */

/**
 * @constructor TableRowElement
 * 
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates a table row element object which contains 
 *       information obout a tr element in a table
 *          
 * @param  {DOMElement}  dom_element  - Reference to the dom element object associated with tr element 
 * @param  {TableInfo}   table_info   - Information about the current table relationships in the DOM
 * 
 * @property  {DOMElement}  dom_element  - Reference to the dom element object associated with tr element 
 * @property  {String}      cache_id     - String that uniquely identifies the cache element object in the cache
 *
 * @property  {Array}         child_cache_elements  - Array of table cache elements for the tree representation of the table
 * @property  {TableElement}  parent_table_element  - Reference to the table element object that contatins the tr element
 *
 * @property  {Number}  type               - Constant indicating the type of table cache element object
 * @property  {String}  cache_id           - String that uniquely identifies the cache element in the DOMCache
 *
 * @property  {Number}  header_cell_count  - Number of header cells in the row
 * @property  {Number}  data_cell_count    - Number of data cells in the row
 */
 
OpenAjax.a11y.cache.TableRowElement = function (dom_element, table_info) {

  this.dom_element  = dom_element;
  this.cache_id     = "";
  
  this.child_cache_elements = [];
  this.parent_table_element = table_info.table_element;

  this.table_type = OpenAjax.a11y.TABLE.TR_ELEMENT;

  this.header_cell_count = 0;
  this.data_cell_count   = 0;
 
};

/**
 * @method addChild
 *
 * @memberOf OpenAjax.a11y.cache.TableRowElement
 * 
 * @desc Adds a cache table element to the tree representation of the table in the table cache
 *
 * @param  {TableElement | CaptionElement | THeadElement | TBodyElement | TableRowElement | TableCellElement }  table_element  - Cache table element object to add to root of tree of table elements 
 */

OpenAjax.a11y.cache.TableRowElement.prototype.addChild = function (child_object) {

 if (child_object) {
  this.child_cache_elements.push(child_object); 
 }  

}; 

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.TableRowElement
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.TableRowElement.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.TableRowElement
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.TableRowElement.prototype.getAttributes = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  var attributes = this.dom_element.getAttributes();
  
  attributes.push(cache_nls.getLabelAndValueNLS('role', this.dom_element.role));
  
  return attributes;
};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.TableRowElement
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style items
 */

OpenAjax.a11y.cache.TableRowElement.prototype.getStyle = function () {

  return this.dom_element.getStyle();
  
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.TableRowElement
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @return {Array} Returns a array of cache properties
 */

OpenAjax.a11y.cache.TableRowElement.prototype.getCacheProperties = function () {

  return this.dom_element.getCacheProperties();
  
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.TableRowElement
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.TableRowElement.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};

/**
 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.TableRowElement
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event information
 */

OpenAjax.a11y.cache.TableRowElement.prototype.getEvents = function (unsorted) {
   
  return this.dom_element.getEvents();
  
};

/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.TableRowElement
 *
 * @desc Creates a text string representation of the tr element object 
 *
 * @return {String} Returns a text string representation of the tr element object
 */
 OpenAjax.a11y.cache.TableRowElement.prototype.toString = function () {
 
   var str =  "tr: ";
 
   if (this.header_cell_count && this.data_cell_count) {
     if (this.header_cell_count === 1) str += " 1 header cell and ";
     else str += this.header_cell_count + " header cells and ";
 
     if (this.data_cell_count === 1) str += " 1 data cell";
     else str += this.data_cell_count + " data cells";
   }
   else {
     if (this.header_cell_count) {
       if (this.header_cell_count === 1) str += " 1 header cell";
       else str += this.header_cell_count + " header cells";
     }
     else {
       if (this.data_cell_count) {
         if (this.data_cell_count === 1) str += " 1 data cell";
         else str += this.data_cell_count + " data cells";
       }
       else {
         str += " no table cells ";       
       }
     }  
   }

   return str;
 };


/* ---------------------------------------------------------------- */
/*                            TableCellElement                      */
/* ---------------------------------------------------------------- */

/**
 * @constructor TableCellElement
 * 
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Create a table cell element object which contains 
 *       information obout a td or th element in a table
 *          
 * @param  {DOMElement}  dom_element  - Reference to the dom element object associated with td or th element 
 * @param  {TableInfo}   table_info   - Information about the current table relationships in the DOM
 * 
 * @property  {DOMElement}  dom_element  - Reference to the dom element object associated with td or th element 
 * @property  {String}      cache_id     - String that uniquely identifies the cache element object in the cache
 *
 * @property  {Array}            child_cache_elements  - Array of table cache elements for the tree representation of the table
 * @property  {TableElement}     parent_table_element  - Reference to the table element object that contatins the td or th element
 * @property  {TableRowElement}  parent_row_element    - Reference to the table element object that contatins the td or th element
 *
 * @property  {Number}  type               - Constant indicating the type of table cache element object
 *
 * @property  {String}  text_content          - Text content of the element including descendent element content
 * @property  {String}  scope                 - Value of the scope attribute
 * @property  {String}  headers               - Value of the headers attribute
 * @property  {Array}   headers_array         - Array of id values in the headers attribute
 * @property  {String}  header_content        - Text content of calculated headers
 * @property  {Number}  header_source         - How header content was calculated    
 * @property  {Number}  number_of_header_ids  - Number of ids in the headers attribute    
 *
 * @property  {Boolean} has_spans          - Value of the rowspan attribute
 * @property  {Number}  row_span           - Value of the rowspan attribute (Note: converted to Number)
 * @property  {Number}  column_span        - Value of the colspan attribute (Note: converted to Number)
 */
 
OpenAjax.a11y.cache.TableCellElement = function (dom_element, table_info) {

  var headers_array = [];  // array of id headers
  var is_th;
   
  this.dom_element  = dom_element;
  this.cache_id     = "";
  
  this.parent_table_element = table_info.table_element;
  this.parent_row_element   = table_info.table_row_element;
  
  this.child_cache_elements = [];   
  
  var text_content = dom_element.getText();   
  this.text_content = text_content;
  if (typeof this.text_content === 'string') this.text_content_for_comparison = text_content.normalizeSpace().toLowerCase();
  else this.text_content_for_comparison = "";
    
  this.table_type = OpenAjax.a11y.TABLE.TD_ELEMENT;

  is_th = dom_element.tag_name == 'th';
  this.scope = dom_element.node.getAttribute('scope');   
  
  if (is_th) {
    this.table_type = OpenAjax.a11y.TABLE.TH_ELEMENT;
  }
  else {
    if (this.scope) {
      this.scope = this.scope.toLowerCase();
      
      if (this.scope == 'row' || this.scope == 'col') {
       this.table_type = OpenAjax.a11y.TABLE.TH_ELEMENT;     
      }
    }
  }

  if (table_info.table_row_element) {
    if (this.table_type === OpenAjax.a11y.TABLE.TD_ELEMENT) {
      table_info.table_row_element.data_cell_count++;
    }
    else{
      table_info.table_row_element.header_cell_count++;    
    }
  }  

  this.headers = dom_element.node.getAttribute('headers');

  this.number_of_header_ids = 0;
  
  if (this.headers && this.headers.length > 0) {
    this.headers_array = this.headers.split(" ");
    
    this.number_of_header_ids = this.headers_array.length;    
  }
   
  this.row_span   = dom_element.node.getAttribute('rowspan');
   
  if (typeof this.row_span === 'string') { 
    this.has_spans = true;
    this.row_span   = parseInt(this.row_span,10);
  } 
  else {
    this.row_span   = 1;
  }
  
  this.column_span   = dom_element.node.getAttribute('colspan');    
   
  if (typeof this.column_span === 'string') { 
    this.has_spans = true;
    this.column_span   = parseInt(this.column_span,10);
  } else {
    this.column_span   = 1;
  }
  
}; 


/**
 * @method addChild
 *
 * @memberOf OpenAjax.a11y.cache.TableCellElement
 * 
 * @desc Adds a cache table element to the tree representation of the table in the cache
 *
 * @param  {TableElement | CaptionElement | THeadElement | TBodyElement | TableRowElement | TableCellElement }  table_element  - Cache table element object to add to root of tree of table elements 
 */

OpenAjax.a11y.cache.TableCellElement.prototype.addChild = function (table_element) {

 if (table_element) {
  this.child_cache_elements.push(table_element); 
 }  

}; 

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.TableCellElement
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.TableCellElement.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.TableCellElement
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style items
 */

OpenAjax.a11y.cache.TableCellElement.prototype.getStyle = function () {

  return this.dom_element.getStyle();
  
};


/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.TableCellElement
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.TableCellElement.prototype.getAttributes = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  var attributes = this.dom_element.getAttributes();
  
  cache_nls.addPropertyIfDefined(attributes, this, 'tag_name');
  cache_nls.addPropertyIfDefined(attributes, this, 'row_span');
  cache_nls.addPropertyIfDefined(attributes, this, 'column_span');
  cache_nls.addPropertyIfDefined(attributes, this, 'headers');
  cache_nls.addPropertyIfDefined(attributes, this, 'scope');
  cache_nls.addPropertyIfDefined(attributes, this, 'role');
  
  if (!unsorted) this.dom_element.sortItems(attributes);
  
  return attributes;
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.TableCellElement
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @return {Array} Returns a array of cache properties
 */

OpenAjax.a11y.cache.TableCellElement.prototype.getCacheProperties = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;
  
  var properties = this.dom_element.getCacheProperties();
  
  cache_nls.addPropertyIfDefined(properties, this, 'table_type');
  cache_nls.addPropertyIfDefined(properties, this, 'header_content');
  cache_nls.addPropertyIfDefined(properties, this, 'header_source');
  cache_nls.addPropertyIfDefined(properties, this, 'text_content');
  
  this.dom_element.sortItems(properties);
  
  return properties;
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.TableCellElement
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.TableCellElement.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};


/**
 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.TableCellElement
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event information
 */

OpenAjax.a11y.cache.TableCellElement.prototype.getEvents = function (unsorted) {
   
  return this.dom_element.getEvents();
  
};

/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.TableCellElement
 *
 * @desc Creates a text string representation of the table cell element object 
 *
 * @return {String} Returns a text string representation of the table cell element object
 */
OpenAjax.a11y.cache.TableCellElement.prototype.toString = function () {
  var text = this.dom_element.getText();
  var tag_name = this.dom_element.tag_name;
  
  if (this.parent_table_element.table_role === OpenAjax.a11y.TABLE_ROLE.DATA) {

    if (text.length) {
      return tag_name + ": " + text;
    }
    else {
      return tag_name + ": empty cell";
    }
  }
  else {
    var str = tag_name + "(for layout) contains: ";
    
    var count = this.dom_element.getElementCount();
    
    if (count === 1) str += "1 element and ";
    else str += count + " elements and ";    
    
    count = text.length;
    
    if (count === 1) str += "1 character";
    else str += count + " characters";    

    return str;    
  }
};

/* ---------------------------------------------------------------- */
/*                       PageElementLayout                               */ 
/* ---------------------------------------------------------------- */

/**
 * @constructor PageElementLayout
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates a body element object used to hold information about a title element
 *
 * @param  {DOMelement}   dom_element      - The dom element object representing the heading element 
 * @param  {MainElement}  parent_landmark  - This is always null since this is the root element
 *
 * @property  {DOMElement}   dom_element      - Reference to the dom element representing the optgroup element
 * @property  {String}       cache_id         - String that uniquely identifies the cache element object in the cache
 * @property  {Number}       document_order   - Ordinal position of the title and main cache items in the document to other title and main cache items
 *
 * @property  {Array}  child_cache_elements  - List of child cache title element, main landmarks and h1 heading element objects as part of cache title and main elements tree  
 *
 * @property  {Boolean}  is_page_element  -  Boolean indicating the element is a page element 
 *
 */

OpenAjax.a11y.cache.PageElementLayout = function (dom_element) {

  this.dom_element     = dom_element;
  this.cache_id        = "page_layout";
  this.document_order  = 0;
  this.is_page_element = true;

  this.child_cache_elements = []; // this is always empty for the body element

}; 

/**
 * @method addChildMainElement
 *
 * @memberOf OpenAjax.a11y.cache.PageElementLayout
 *
 * @desc Adds a main landmark  object to the tree of title and main elements  
 *
 * @param {MainElement}  main_element  -  Main landmark element object to add 
 */

OpenAjax.a11y.cache.PageElementLayout.prototype.addChildMainElement = function (main_element) {

  if (main_element) {
    this.child_cache_elements.push(main_element); 
  }  

};

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.PageElementLayout
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.PageElementLayout.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.PageElementLayout
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style display objects
 */

OpenAjax.a11y.cache.PageElementLayout.prototype.getStyle = function () {

  return this.dom_element.getStyle();
  
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.PageElementLayout
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of attribute display object
 */

OpenAjax.a11y.cache.PageElementLayout.prototype.getAttributes = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  var attributes = this.dom_element.getAttributes();
  
  cache_nls.addPropertyIfDefined(attributes, this, 'role');
  
  if (!unsorted) this.dom_element.sortItems(attributes);
  
  return attributes;
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.PageElementLayout
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of cache property display object
 */

OpenAjax.a11y.cache.PageElementLayout.prototype.getCacheProperties = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;

  var properties = this.dom_element.getCacheProperties(unsorted);
 
  if (!unsorted) this.dom_element.sortItems(properties);

  return properties;
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.PageElementLayout
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.PageElementLayout.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};



/**
 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.PageElementLayout
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event item display objects
 */

OpenAjax.a11y.cache.PageElementLayout.prototype.getEvents = function () {
   
  return this.dom_element.getEvents();
  
};

/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.PageElementLayout
 *
 * @desc Returns a text string representation of the title element 
 *
 * @return {String} Returns string represention the title element object
 */
  
OpenAjax.a11y.cache.PageElementLayout.prototype.toString = function () {
  return "page";  
};
/*
 * Copyright 2011-2013 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* ---------------------------------------------------------------- */
/*                            TextCache                            */
/* ---------------------------------------------------------------- */

/**
 * @constructor TextCache
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates cache object representing information related to text nodes in a document
 *
 * @param {DOMCache}   dom_cache   - Reference to the DOMCache object 
 * 
 * @property {DOMCache} dom_cache  - Reference to the DOMCache object 
 *         
 * @property {Boolean}  up_to_date - Boolean true if the cache has been creating using the current DOMElements, else false
 *                                   NOTE: This is a common property of all caches and is used when selectively build caches 
 *                                         based on whether a rule needs the cache
 *
 * @property {Array}    text_nodes     - List of text nodes in the document 
 * @property {Number}   length         - Number of image element objects in the list 
 */
  
OpenAjax.a11y.cache.TextCache = function (dom_cache) {

  this.dom_cache = dom_cache;
  this.up_to_date = false;
 
  this.text_nodes = [];
  this.length = 0;
 
}; 

/**
 * @method addTextNode
 * 
 * @memberOf OpenAjax.a11y.cache.TextCache
 *
 * @desc Adds a text node to the list of image elements and generates a cache id for the object.
 *
 * @param  {DOMText}  text_node  - text_node object to add 
 *
 * @return {Number} Returns the length of the list of image element objects
 */

OpenAjax.a11y.cache.TextCache.prototype.addTextNode = function (text_node) {


  // item must exist and have the position property
  if (text_node) {
  
    var pe = text_node.parent_element;
    
    if (pe.tag_name !== 'script' && pe.tag_name !== 'style' &&  pe.tag_name !== 'object') {
      this.length = this.length + 1;
      text_node.document_order = this.length;
      this.text_nodes.push(text_node);
    }  
  } 

  return this.length;

};


/**
 * @deprecated getTextNodeByCacheId
 * 
 * @memberOf OpenAjax.a11y.cache.TextCache
 *
 * @desc Finds the the text node object with the matching cache id
 *
 * @param  {String}  cache_id  - Cache id of image element object
 *
 * @return {DOMText | null} Returns cache text node object if cache id is found, otherwise null
 */

OpenAjax.a11y.cache.TextCache.prototype.getTextNodeByCacheId = function (cache_id) {
  return this.getItemByCacheId(cache_id);
};

/**
 * @method getItemByCacheId
 * 
 * @memberOf OpenAjax.a11y.cache.TextCache
 *
 * @desc Finds the the text node object with the matching cache id
 *
 * @param  {String}  cache_id  - Cache id of text node object
 *
 * @return {ImageElement | null} Returns cache text node object if cache id is found, otherwise null
 */

OpenAjax.a11y.cache.TextCache.prototype.getItemByCacheId = function (cache_id) {

  var i;
  var text_nodes_len = this.text_nodes.length;

  if (cache_id && cache_id.length) {  
    for (i=0; i < text_nodes_len; i++) {
      if (this.text_nodes[i].cache_id == cache_id) {
        return this.text_nodes[i];
      }
    } // end loop
  } 

 return null;
};


/**
 * @method emptyCache
 *
 * @memberOf OpenAjax.a11y.cache.TextCache
 *
 * @desc Resests the TextCache object properties and empties all the lists and arrays 
 */

OpenAjax.a11y.cache.TextCache.prototype.emptyCache = function () {

  this.text_nodes.length = 0;
  this.up_to_date = false;

};

/**
 * @method updateCacheItems
 *
 * @memberOf OpenAjax.a11y.cache.TextCache
 *
 * @desc Updates the images cache object by checking to see if a dom element
 *          should be added to the cache
 *  
 * @param  {DOMElement}   dom_element   - dom element object to check for inclusion in images cache
 */
 
OpenAjax.a11y.cache.TextCache.prototype.updateCacheItems = function (dom_element) {

  if (dom_element.type === Node.TEXT_NODE) {
  
//    OpenAjax.a11y.logger.debug("  ADDED TEXT: " + dom_element.text );
  
    this.addTextNode(dom_element);
  
  }
  
};

/**
 * @method traverseDOMElementsForTextNodes
 *
 * @memberOf OpenAjax.a11y.cache.TextCache
 *
 * @desc Traverses DOM Element objects in the tree to update the text cache 
 *
 * @param  {DOMElement}  dom_element - dom element object to check for inclusion in images cache
 */
 
OpenAjax.a11y.cache.TextCache.prototype.traverseDOMElementsForTextNodes = function (dom_element) {

  if (!dom_element) return;

  if (dom_element.type == Node.ELEMENT_NODE) {

    for (var i = 0; i < dom_element.child_dom_elements.length; i++ ) {
      this.traverseDOMElementsForTextNodes(dom_element.child_dom_elements[i]);
    } // end loop
  }
  else {
     this.updateCacheItems(dom_element);
  }
  
}; 

/**
 * @method updateCache
 *
 * @memberOf OpenAjax.a11y.cache.TextCache
 *
 * @desc Traverses the DOMElements to update the text cache
 *       NOTE: This function is only used when the specialized caches
 *       are build as rules need them.  In this condition, if the rules 
 *       dependent on the links cache are disabled, this cache would 
 *       not be updated
 */
 
OpenAjax.a11y.cache.TextCache.prototype.updateCache = function () {
  var i;
  var children = this.dom_cache.element_cache.child_dom_elements;
  var children_len = children.length;
 
  for (i=0; i < children_len; i++) {
    this.traverseDOMElementsForTextNodes(children[i]);
  }  

  this.up_to_date = true;
};

/*
 * Copyright 2011-2013 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* ---------------------------------------------------------------- */
/*                            KeyboardFocus                             */
/* ---------------------------------------------------------------- */

/**
 * @constructor KeyboardFocus
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates cache object representing information related to links in a web page
 *
 * @param {DOMCache}   dom_cache   - Reference to the DOMCache object 
 * 
 * @property {DOMCache} dom_cache  - Reference to the DOMCache object       
 * @property {Boolean}  up_to_date - Boolean true if the cache has been creating using the current DOMElements, else false
 *                                   NOTE: This is a common property of all caches and is used when selectively build caches 
 *                                         based on whether a rule needs the cache
 *
 * @property {Array}    area_elements  - List of area element objects in the document 
 * @property {Array}    link_elements  - List of link element objects in the document 
 * @property {Number}   length         - Number of link element objects in the list 
 *
 * @property {String}   this.sort_property   - Link element object property the list of link objects is sorted by
 * @property {Boolean}  this.sort_ascending  - true if list is sorted in ascending order, otherwise false
 *
 * @property {Array}    links_sorted_by_href  - List of link element object sorted by href values;
 * @property {Array}    links_sorted_by_name  - List of link element object sorted by there accessible name (i.e link text);
 *  
 * @property {Boolean}  sorted_by_href_ready  - True if list of link element objects sorted by href values is ready for use in rules
 * @property {Boolean}  sorted_by_name_ready  - True if list of link element objects sorted by name values is ready for use in rules
 */

OpenAjax.a11y.cache.KeyboardFocusCache = function (dom_cache) {

  this.dom_cache = dom_cache;
  this.up_to_date = false;
  this.page_element = null;
  this.interactive_elements = [];
  
}; 



/**
 * @method createKeyboardFocusCache
 * 
 * @memberOf OpenAjax.a11y.cache.KeyboardFocusCache
 *
 * @desc Populates the keyboard focus cache from the link, controls and media caches
 */

OpenAjax.a11y.cache.KeyboardFocusCache.prototype.createKeyboardFocusCache = function () {

  OpenAjax.a11y.logger.debug("  Page Element: " + this.dom_cache.element_cache.getPageElement());


  this.page_element = new OpenAjax.a11y.cache.PageElementKeyboardFocus(this.dom_cache.element_cache.getPageElement());
  
  this.interactive_elements = this.dom_cache.links_cache.link_elements;
  this.interactive_elements = this.interactive_elements.concat(this.dom_cache.controls_cache.control_elements);
//  this.interactive_elements = this.interactive_elements.concat(this.dom_cache.controls_cache.widget_elements);
  this.interactive_elements = this.interactive_elements.concat(this.dom_cache.media_cache.object_elements);  
  
};


/**
 * @method getItemByCacheId
 * 
 * @memberOf OpenAjax.a11y.cache.KeyboardFocusCache
 *
 * @desc Finds the the link element object with the matching cache id
 *
 * @param  {String }  cache_id  - Cache id of link element object
 *
 * @return {LinkElement} Returns cache link element object if cache id is found, otherwise null
 */

OpenAjax.a11y.cache.KeyboardFocusCache.prototype.getItemByCacheId = function (cache_id) {

  var i;

  var link_elements_len = this.link_elements.length;

  if (cache_id && cache_id.length) {  
   for (i=0; i < link_elements_len; i++) {
     if (this.link_elements[i].cache_id == cache_id) {
       return this.link_elements[i];
     }
   } // end loop
 } 

 return null;
};

/**
 * @method emptyCache
 *
 * @memberOf OpenAjax.a11y.cache.KeyboardFocusCache
 *
 * @desc Resests the KeyboardFocus object properties and empties all the lists and arrays 
 */
 
OpenAjax.a11y.cache.KeyboardFocusCache.prototype.emptyCache = function () {

  this.interactive_elements = [];
  this.length = 0;
  this.up_to_date = false;

};


/* ---------------------------------------------------------------- */
/*                       PageElementKeyboardFocus                               */ 
/* ---------------------------------------------------------------- */

/**
 * @constructor PageElementKeyboardFocus
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @desc Creates a body element object used to hold information about a title element
 *
 * @param  {DOMelement}   dom_element      - The dom element object representing the page element 
 */

OpenAjax.a11y.cache.PageElementKeyboardFocus = function (dom_element) {

  this.dom_element     = dom_element;
  
}; 

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.cache.PageElementKeyboardFocus
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.cache.PageElementKeyboardFocus.prototype.getNodeResults = function () {
  return this.dom_element.getNodeResults();
};

/**
 * @method getStyle
 *
 * @memberOf OpenAjax.a11y.cache.PageElementKeyboardFocus
 *
 * @desc Returns an array of style items 
 *
 * @return {Array} Returns a array of style display objects
 */

OpenAjax.a11y.cache.PageElementKeyboardFocus.prototype.getStyle = function () {

  return this.dom_element.getStyle();
  
};

/**
 * @method getAttributes
 *
 * @memberOf OpenAjax.a11y.cache.PageElementKeyboardFocus
 *
 * @desc Returns an array of attributes for the element, sorted in alphabetical order 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of attribute display object
 */

OpenAjax.a11y.cache.PageElementKeyboardFocus.prototype.getAttributes = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;
  var attributes = this.dom_element.getAttributes();
  
//  cache_nls.addPropertyIfDefined(attributes, this, 'tag_name');
  
  if (!unsorted) this.dom_element.sortItems(attributes);
  
  return attributes;
};

/**
 * @method getCacheProperties
 *
 * @memberOf OpenAjax.a11y.cache.PageElementKeyboardFocus
 *
 * @desc Returns an array of cache properties sorted by property name 
 *
 * @param {Boolean}  unsorted  - If defined and true the results will NOT be sorted alphabetically
 *
 * @return {Array} Returns a array of cache property display object
 */

OpenAjax.a11y.cache.PageElementKeyboardFocus.prototype.getCacheProperties = function (unsorted) {

  var cache_nls = OpenAjax.a11y.cache_nls;

  var properties = this.dom_element.getCacheProperties(unsorted);
 
  if (!unsorted) this.dom_element.sortItems(properties);

  return properties;
};

/**
 * @method getCachePropertyValue
 *
 * @memberOf OpenAjax.a11y.cache.PageElementKeyboardFocus
 *
 * @desc Returns the value of a property 
 *
 * @param {String}  property  - The property to retreive the value
 *
 * @return {String | Number} Returns the value of the property
 */

OpenAjax.a11y.cache.PageElementKeyboardFocus.prototype.getCachePropertyValue = function (property) {

  if (typeof this[property] == 'undefined') {
    return this.dom_element.getCachePropertyValue(property);
  }
  
  return this[property];
};



/**
 * @method getEvents
 *
 * @memberOf OpenAjax.a11y.cache.PageElementKeyboardFocus
 *
 * @desc Returns an array of events for the element, sorted in alphabetical order 
 *
 * @return {Array} Returns a array of event item display objects
 */

OpenAjax.a11y.cache.PageElementKeyboardFocus.prototype.getEvents = function () {
   
  return this.dom_element.getEvents();
  
};

/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.cache.PageElementKeyboardFocus
 *
 * @desc Returns a text string representation of the title element 
 *
 * @return {String} Returns string represention the title element object
 */
  
OpenAjax.a11y.cache.PageElementKeyboardFocus.prototype.toString = function () {
  return "page";  
};


  /*
 * Copyright 2011-2013 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
 

/* ---------------------------------------------------------------- */
/*                       ElementInformation                         */
/* ---------------------------------------------------------------- */

/**
 * @constructor ElementInformation
 *
 * @memberOf OpenAjax.a11y.cache
 *
 * @param  {Object}  dom_cache  -  DOMCache object used for evaluation
 *
 */ 
 
/**
 * @private
 * @constructor Internal Properties
 *
 */
 
OpenAjax.a11y.cache.ElementInformation = function () {

  // General page element counts and fram usage

  this.all_element_count    = 0;
  this.all_hidden_count     = 0;
  this.all_offscreen_count  = 0;
  
  this.frame_count      = 0;
  this.iframe_count     = 0;
  
  // Image counts
  
  this.all_images_count = 0;
  this.img_count        = 0;
  this.canvas_count     = 0;
  this.svg_count        = 0;
  this.figure_count     = 0;
  this.figcaption_count = 0;
  this.alt_attribute_count      = 0;
  this.longdesc_attribute_count = 0;

  // heading level counts

  this.all_headings_count = 0;
  this.h1_count           = 0;
  this.h2_count           = 0;
  this.h3_count           = 0;
  this.h4_count           = 0;
  this.h5_count           = 0;
  this.h6_count           = 0;
  
  // HTML5 Section Element counts
  
  this.all_sections_count = 0;
  this.address_count      = 0;
  this.article_count      = 0;
  this.aside_count        = 0;
  this.details_count      = 0;
  this.dialog_count       = 0;
  this.header_count       = 0;
  this.footer_count       = 0;
  this.main_count         = 0;
  this.nav_count          = 0;
  this.section_count      = 0;
  this.summary_count      = 0;
    
  // List element counts  
    
  this.all_lists_count = 0;
  this.ul_count        = 0;
  this.ol_count        = 0;
  this.li_count        = 0;
  
  this.dl_count       = 0;
  this.dt_count       = 0;
  this.dd_count       = 0;
  
  this.all_links_count  = 0;
  this.a_count          = 0;
  this.area_count       = 0;

  // Table element counts

  this.all_tables_count  = 0;
  this.table_count       = 0;
  this.th_count          = 0;
  this.td_count          = 0;
  this.caption_count     = 0;
  this.summary_attribute_count    = 0;
  this.scope_attribute_count      = 0;
  this.headers_attribute_count    = 0;
  
  // Form control counts

  this.all_forms_count      = 0;
  this.input_button_count   = 0;
  this.input_checkbox_count = 0;
  this.input_color_count    = 0;
  this.input_date_count     = 0;
  this.input_datetime_count = 0;
  this.input_email_count    = 0;
  this.input_file_count     = 0;
  this.input_image_count    = 0;
  this.input_month_count    = 0;
  this.input_number_count   = 0;
  this.input_password_count = 0;
  this.input_radio_count    = 0;
  this.input_range_count    = 0;
  this.input_reset_count    = 0;
  this.input_search_count   = 0;
  this.input_submit_count   = 0;
  this.input_text_count     = 0;
  this.input_tel_count      = 0;
  this.input_time_count     = 0;
  this.input_url_count      = 0;
  this.input_week_count     = 0;
  
  this.autofocus_attribute_count   = 0;
  this.placeholder_attribute_count = 0;
  this.pattern_attribute_count     = 0;
  this.required_attribute_count    = 0;

  this.output_count    = 0;  
  this.progress_count  = 0;  
  this.meter_count     = 0;  
  
  this.label_count     = 0;
  this.fieldset_count  = 0;
  this.legend_count    = 0;
  this.title_attribute_count    = 0;
  
  // media counts
  
  this.button_count      = 0;
  this.select_count      = 0;
  this.textarea_count    = 0;
  
  this.all_media_count  = 0;
  this.audio_count      = 0;
  this.embed_count      = 0;
  this.object_count     = 0;
  this.video_count      = 0;
  this.track_count      = 0;
  this.source_count     = 0;
  
  // ARIA Landmark counts

  this.all_landmarks_count      = 0;  
  
  this.role_application_count   = 0;
  this.role_banner_count        = 0;
  this.role_complementary_count = 0;
  this.role_contentinfo_count   = 0;
  this.role_form_count          = 0;
  this.role_main_count          = 0;
  this.role_navigation_count    = 0;
  this.role_region_count        = 0;
  this.role_search_count        = 0;

  // ARIA Structure counts

  this.all_structures_count    = 0;
  
  this.role_article_count      = 0;
  this.role_directory_count    = 0;
  this.role_document_count     = 0;  
  this.role_heading_count      = 0;
  this.role_math_count         = 0;
  this.role_note_count         = 0;
  this.role_presentation_count = 0;

  // ARIA Widget counts

  this.all_widgets_count   = 0;
  
  this.role_button_count   = 0;
  this.role_link_count     = 0;

  this.aria_describedby_count = 0;
  this.aria_describedat_count = 0;

  this.aria_labelledby_count  = 0;
  this.aria_label_count       = 0;
  
  this.aria_owns_count       = 0;
  this.aria_activedescendant_count = 0;
  this.aria_flowto_count     = 0;
  this.aria_controls_count   = 0;
  this.aria_hidden_count     = 0; 

  // ARIA Live Region counts

  this.all_lives_count    = 0;   
  
  this.aria_live_count    = 0; 
  this.role_alert_count   = 0; 
  this.role_log_count     = 0; 
  this.role_status_count  = 0; 

  // Event counts

  this.all_events_count      = 0; 

  this.load_event_count      = 0; 
  
  this.blur_event_count      = 0; 
  this.focus_event_count     = 0; 
  
  this.change_event_count    = 0; 
  
  this.drag_events_count     = 0; 
  
  this.click_event_count        = 0; 
  this.double_click_event_count = 0; 
  
  this.keyboard_events_count  = 0; 
  this.mouse_events_count     = 0; 
  this.touch_events_count     = 0; 
  this.pointer_events_count   = 0; 


};

/**
 * @method countElement
 *
 * @memberOf OpenAjax.a11y.cache.ElementInformation
 *
 * @desc Counts elements of interest
 *
 * @param  {Object}  dom_element  - DOM element object
 */
 
OpenAjax.a11y.cache.ElementInformation.prototype.countElement = function (dom_element) {

  var tag_name = dom_element.tag_name;
  var node = dom_element.node;
  var cs   = dom_element.computed_style;

  this.all_element_count++;
  
  if (cs.is_visible_onscreen === OpenAjax.a11y.VISIBILITY.HIDDEN) { 
    this.all_hidden_count++;
    if (cs.is_visible_at === OpenAjax.a11y.VISIBILITY.VISIBLE) {
      this.all_offscreen_count++;
    }
  }  
    
  switch (tag_name) {
  
  case 'frame':
    this.frame_count++;
    break;
    
  case 'iframe':
    this.iframe_count++;
    break;
    
  case 'img':
    this.img_count++;
    this.all_images_count++;
    
    if (dom_element.has_alt)      this.alt_attribute_count++;
    if (dom_element.has_longdesc) this.longdesc_attribute_count++;
    
    break;
    
  case 'canvas':
    this.canvas_count++;
    this.all_images_count++;
    break;

  case 'svg':
    this.svg_count++;
    this.all_images_count++;
    break;
    
  case 'figure':
    this.figure_count++;
    this.all_images_count++;
    break;
    
  case 'figcaption':
    this.figcaption_count++;
    this.all_images_count++;
    break;
        
  case 'h1':
    this.h1_count++;
    this.all_headings_count++;
    break;
    
  case 'h2':
    this.h2_count++;
    this.all_headings_count++;
    break;
    
  case 'h3':
    this.h3_count++;
    this.all_headings_count++;
    break;
    
  case 'h4':
    this.h4_count++;
    this.all_headings_count++;
    break;
    
  case 'h5':
    this.h5_count++;
    this.all_headings_count++;
    break;
    
  case 'h6':
    this.h6_count++;
    this.all_headings_count++;
    break;
        
  case 'header':
    this.header_count++;
    this.all_sections_count++;
    break;
    
  case 'footer':
    this.footer_count++;
    this.all_sections_count++;
    break;
    
  case 'section':
    this.section_count++;
    this.all_sections_count++;
    break;
    
  case 'address':
    this.address_count++;
    this.all_sections_count++;
    break;
    
  case 'article':
    this.article_count++;
    this.all_sections_count++;
    break;
    
  case 'aside':
    this.aside_count++;
    this.all_sections_count++;
    break;
    
  case 'details':
    this.details_count++;
    this.all_sections_count++;
    break;
    
  case 'dialog':
    this.dialog_count++;
    this.all_sections_count++;
    break;
    
  case 'summary':
    this.summary_count++;
    this.all_sections_count++;
    break;
    
  case 'main':
    this.main_count++;
    this.all_sections_count++;
    break;
    
  case 'nav':
    this.nav_count++;
    this.all_sections_count++;
    break;
    
  case 'ul':
    this.ul_count++;
    this.all_lists_count++;
    break;
    
  case 'ol':
    this.ol_count++;
    this.all_lists_count++;
    break;
    
  case 'li':
    this.li_count ++;
    this.all_lists_count++;
    break;
    
  case 'dl':
    this.dl_count++;
    this.all_lists_count++;
    break;
    
  case 'dt':
    this.dt_count++;
    this.all_lists_count++;
    break;
    
  case 'dd':
    this.dd_count++;
    this.all_lists_count++;
    break;
    
  case 'a':
    this.a_count  ++;
    this.all_links_count++;
    break;
    
  case 'area':
    this.area_count       = 0;
    this.all_links_count++;
    break;

  case 'table':
    this.table_count++;
    this.all_tables_count++;

    if (dom_element.has_summary) this.summary_attribute_count++;

    break;
        
  case 'th':
  case 'td':
    if (tag_name == 'th') this.th_count ++;
    else this.td_count ++;
    
    this.all_tables_count++;
        
    if (dom_element.has_scope)   this.scope_attribute_count++;
    if (dom_element.has_headers) this.headers_attribute_count++;

    break;
    
  case 'caption':
    this.caption_count++;
    this.all_tables_count++;
    break;
    
  case 'input':
  
    var type = dom_element.node.getAttribute('type');

    if (dom_element.has_autofocus)   this.autofocus_attribute_count++;
    if (dom_element.has_required)    this.required_attribute_count++;
    if (dom_element.has_pattern)     this.pattern_attribute_count++;
    if (dom_element.has_placeholder) this.placeholder_attribute_count++;
    if (dom_element.has_title)       this.title_attribute_count++;

    switch (type) {
    
    case 'checkbox':
      this.input_checkbox_count++;
      this.all_forms_count++;
      break;
    
    case 'radio':
      this.input_radio_count++;
      this.all_forms_count++;
      break;
    
    case 'button':
      this.input_button_count++;
      this.all_forms_count++;
      break;
    
    case 'color':
      this.input_color_count++;
      this.all_forms_count++;
      break;
    
    case 'date':
      this.input_date_count++;
      this.all_forms_count++;
      break;
    
    case 'datetime':
      this.input_datetime_count++;
      this.all_forms_count++;
      break;
    
    case 'email':
      this.input_email_count++;
      this.all_forms_count++;
      break;
    
    case 'file':
      this.input_file_count++;
      this.all_forms_count++;
      break;
    
    case 'image':
      this.input_image_count++;
      this.all_forms_count++;
      break;
    
    case 'month':
      this.input_month_count++;
      this.all_forms_count++;
      break;
    
    case 'number':
      this.input_number_count++;
      this.all_forms_count++;
      break;
    
    case 'password':
      this.input_password_count++;
      this.all_forms_count++;
      break;
    
    case 'range':
      this.input_range_count++;
      this.all_forms_count++;
      if (node.required) this.required_attribute_count++;
      break;
    
    case 'reset':
      this.input_reset_count++;
      this.all_forms_count++;
      break;
    
    case 'search':
      this.input_search_count++;
      this.all_forms_count++;
      break;
    
    case 'submit':
      this.input_submit_count++;
      this.all_forms_count++;
      break;
    
    case 'tel':
      this.input_tel_count++;
      this.all_forms_count++;
      break;
    
    case 'text':
      this.input_text_count++;
      this.all_forms_count++;
      break;
    
    case 'time':
      this.input_time_count++;
      this.all_forms_count++;
      break;
    
    case 'url':
      this.input_url_count++;
      this.all_forms_count++;
      break;
    
    case 'week':
      this.input_week_count ++;
      this.all_forms_count++;
      break;
   
    default:
      break;
   }
   
   case 'output':
    this.output_count++;
    this.all_forms_count++;
    if (dom_element.has_title)   this.title_attribute_count++;
    break;
    
  case 'progress':
    this.progress_count++;
    this.all_forms_count++;
    if (dom_element.has_title)   this.title_attribute_count++;
    break;
    
  case 'meter':
    this.meter_count++;
    this.all_forms_count++;
    if (dom_element.has_title)   this.title_attribute_count++;
    break;
    
  case 'label':
    this.label_count++;
    this.all_forms_count++;
    break;
    
  case 'fieldset':
    this.fieldset_count++;
    this.all_forms_count++;
    break;
    
  case 'legend':
    this.legend_count++;
    this.all_forms_count++;
    break;
    
  case 'button':
    this.button_count++;
    this.all_forms_count++;
    if (dom_element.has_title)   this.title_attribute_count++;
    break;
    
  case 'select':
    this.select_count++;
    this.all_forms_count++;
    if (dom_element.has_autofocus)   this.autofocus_attribute_count++;
    if (dom_element.has_required)    this.required_attribute_count++;
    if (dom_element.has_pattern)     this.pattern_attribute_count++;
    if (dom_element.has_placeholder) this.placeholder_attribute_count++;
    if (dom_element.has_title)       this.title_attribute_count++;
    break;
    
  case 'textarea':
    this.textarea_count++;
    this.all_forms_count++;
    if (dom_element.has_autofocus)   this.autofocus_attribute_count++;
    if (dom_element.has_required)    this.required_attribute_count++;
    if (dom_element.has_pattern)     this.pattern_attribute_count++;
    if (dom_element.has_placeholder) this.placeholder_attribute_count++;
    if (dom_element.has_title)       this.title_attribute_count++;

    break;
  
  // Media elements  
    
  case 'audio':
    this.audio_count++;
    this.all_media_count++;
    break;
    
  case 'embed':
    this.embed_count++;
    this.all_media_count++;
    break;
    
  case 'object':
    this.object_count++;
    this.all_media_count++;
    break;
    
  case 'video':
    this.video_count++;
    this.all_media_count++;
    break;
    
  case 'track':
    this.track_count++;
    this.all_media_count++;
    break;
    
  case 'source':
    this.source_count++;
    this.all_media_count++;
    break;
  
  default:
    break;  
  
  }


  if (dom_element.has_role) {
  
    var role = dom_element.role;

    switch (role) {
  
      // Landmark roles

      case 'application':
        this.role_banner_count++;
        this.all_landmarks_count++;
        break;
      
      case 'banner':
        this.role_banner_count++;
        this.all_landmarks_count++;
        break;

      case 'complementary':
        this.role_complementary_count++;
        this.all_landmarks_count++;
        break;

      case '':
        this.role__count++;
        this.all_landmarks_count++;
        break;

      case 'contentinfo':
        this.role_contentinfo_count++;
        this.all_landmarks_count++;
        break;

      case 'form':
        this.role_form_count++;
        this.all_landmarks_count++;
        break;

      case 'main':
        this.role_main_count++;
        this.all_landmarks_count++;
        break;

      case 'navigation':
        this.role_navigation_count++;
        this.all_landmarks_count++;
        break;

      case 'region':
        this.role_region_count++;
        this.all_landmarks_count++;
        break;

      case 'search':
        this.role_search_count++;
        this.all_landmarks_count++;
        break;

      // Structure roles

      case 'article':
        this.role_article_count++;
        this.all_structures_count++;
        break;
      
      case 'directory':
        this.role_directory_count++;
        this.all_structures_count++;
        break;
      
      case 'document':
        this.role_document_count++;
        this.all_structures_count++;
        break;
      
      case 'heading':
        this.role_heading_count++;
        this.all_structures_count++;
        break;
      
      case 'math':
        this.role_math_count++;
        this.all_structures_count++;
        break;
      
      case 'note':
        this.role_note_count++;
        this.all_structures_count++;
        break;
      
      case 'presentation':
        this.role_presentation_count++;
        this.all_structures_count++;
        break;

      // Live region roles

      case 'alert':
        this.role_alert_count++;
        this.all_lives_count++;
        break;

      case 'log':
        this.role_log_count++;
        this.all_lives_count++;
        break;

      case 'status':
        this.role_status_count++;
        this.all_lives_count++;
        break;

      // Widget roles

      case 'button':
        this.role_button_count++;
        this.all_widgets_count++;
        break;

      case 'link':
        this.role_link_count++;
        this.all_widgets_count++;
        break;

      default:
        this.all_widget_count++;
        break;
    }  

  }

  // Check for ARIA attribute related content
  
  if (dom_element.has_aria_describedby) this.aria_describedby_count++;
  if (dom_element.has_aria_describedat) this.aria_describedat_count++;

  if (dom_element.has_aria_labelledby) this.aria_labelledby_count++;
  if (dom_element.has_aria_label)      this.aria_label_count++;
  
  if (dom_element.has_aria_owns)             this.aria_owns_count++;
  if (dom_element.has_aria_activedescendant) this.aria_activedescendant_count++;
  if (dom_element.has_aria_flowto)           this.aria_flowto_count++;
  if (dom_element.has_aria_controls)         this.aria_controls_count++;
  if (dom_element.has_aria_hidden)           this.aria_hidden_count++; 

  if (dom_element.has_aria_live) {
    this.aria_live_count++; 
    this.all_lives_count++; 
  }  

  // Check for events
  
  var events = dom_element.events;
  
  if (events.has_blur)  {
    this.blur_event_count++;
    this.all_events_count++;
  }  
  
  if (events.has_change)  {
    this.change_event_count++;
    this.all_events_count++;
  }  
  
  if (events.has_click)  {
    this.click_event_count++;
    this.all_events_count++;
  }  
  
  if (events.has_double_click)  {
    this.double_click_event_count++;
    this.all_events_count++;
  }  
  
  if (events.has_focus)  {
    this.focus_event_count++;
    this.all_events_count++;
  }  
  
  if (events.has_load)  {
    this.load_event_count++;
    this.all_events_count++;
  }  
  
  if (events.has_key_down ||
      events.has_key_press||
      events.has_key_up) {
    this.keyboard_events_count++;
    this.all_events_count++;
  }  
   
  if (events.has_mouse_down  ||
      events.has_mouse_up    ||
      events.has_mouse_move  ||
      events.has_mouse_out   ||
      events.has_mouse_over  ||
      events.has_mouse_enter ||
      events.has_mouse_leave) {
    this.mouse_events_count++;
    this.all_events_count++;
  }  

  if (events.has_drag ||
      events.has_drag_end||
      events.has_drag_enter ||
      events.has_drag_leave ||
      events.has_drag_over ||
      events.has_drag_start ||
      events.has_drop) {
    this.drag_events_count++;
    this.all_events_count++;
  }  

  if (events.has_pointer_up ||
      events.has_pointer_cancel ||
      events.has_pointer_move ||
      events.has_pointer_over ||
      events.has_pointer_out ||
      events.has_pointer_enter ||
      events.has_pointer_leave) {
    this.pointer_events_count++;
    this.all_events_count++;
  }  

  if (events.has_touch_start ||
      events.has_touch_end ||
      events.has_touch_leave ||
      events.has_touch_move ||
      events.has_touch_cancel ) {
    this.touch_events_count++;
    this.all_events_count++;
  }  

};

/**
 * @method toJSON
 *
 * @memberOf OpenAjax.a11y.cache.ElementInformation
 *
 * @desc Creates a string representing the element information on a web page in a JSON format
 *
 * @param  {Boolean}  add_comma  - if true add comma to end of JSON object
 * @param  {String}   prefix     - Optional string of prefix characters (e.g. typically spaces)
 *
 * @return {String} Returns a string in JSON format
 */
 
OpenAjax.a11y.cache.ElementInformation.prototype.toJSON = function (add_comma, prefix) {

  if (typeof add_comma !== 'boolean') add_comma = false;  
  if (typeof prefix    !== 'string')  prefix = "";
  
  var json = "";

  json += prefix + "\"markup_information\": {\n";
  
  json += prefix + "  \"summary\": {\n";
  json += prefix + "    \"all_element_count\"    : " + this.all_element_count   + ",\n";
  json += prefix + "    \"all_hidden_count\"     : " + this.all_hidden_count    + ",\n";
  json += prefix + "    \"all_offscreen_count\"  : " + this.all_offscreen_count + ",\n\n";  
  
  json += prefix + "    \"frame_count\"          : " + this.frame_count  + ",\n";
  json += prefix + "    \"iframe_count\"         : " + this.iframe_count + "\n";
  json += prefix + "  },\n";
  
  json += prefix + "  \"images\": {\n";  
  json += prefix + "    \"all_images_count\"     : " + this.all_images_count + ",\n\n";
  
  json += prefix + "    \"img_count\"            : " + this.img_count        + ",\n";
  json += prefix + "    \"canvas_count\"         : " + this.canvas_count     + ",\n";
  json += prefix + "    \"svg_count\"            : " + this.canvas_count     + ",\n";
  json += prefix + "    \"figure_count\"         : " + this.figure_count     + ",\n";
  json += prefix + "    \"figcaption_count\"     : " + this.figcaption_count + ",\n\n";
  
  json += prefix + "    \"alt_attribute_count\"      : " + this.alt_attribute_count      + ",\n";
  json += prefix + "    \"longdesc_attribute_count\" : " + this.longdesc_attribute_count + "\n";
  json += prefix + "  },\n";
  
  json += prefix + "  \"headings\": {\n";  
  json += prefix + "    \"all_headings_count\"   : " + this.all_headings_count + ",\n\n";
  
  json += prefix + "    \"h1_count\"             : " + this.h1_count + ",\n";
  json += prefix + "    \"h2_count\"             : " + this.h2_count + ",\n";
  json += prefix + "    \"h3_count\"             : " + this.h3_count + ",\n";
  json += prefix + "    \"h4_count\"             : " + this.h4_count + ",\n";
  json += prefix + "    \"h5_count\"             : " + this.h5_count + ",\n";
  json += prefix + "    \"h6_count\"             : " + this.h6_count + "\n";
  json += prefix + "  },\n";
  
  json += prefix + "  \"sections\": {\n";    
  json += prefix + "    \"all_sections_count\"   : " + this.all_sections_count + ",\n\n";
  
  json += prefix + "    \"address_count\"        : " + this.address_count + ",\n";
  json += prefix + "    \"article_count\"        : " + this.article_count + ",\n";
  json += prefix + "    \"aside_count\"          : " + this.aside_count   + ",\n";
  json += prefix + "    \"details_count\"        : " + this.details_count + ",\n";
  json += prefix + "    \"dialog_count\"         : " + this.dialog_count  + ",\n";
  json += prefix + "    \"footer_count\"         : " + this.footer_count  + ",\n";
  json += prefix + "    \"header_count\"         : " + this.header_count  + ",\n";
  json += prefix + "    \"main_count\"           : " + this.main_count    + ",\n";
  json += prefix + "    \"nav_count\"            : " + this.nav_count     + ",\n";
  json += prefix + "    \"section_count\"        : " + this.section_count + ",\n";
  json += prefix + "    \"summary_count\"        : " + this.summary_count + "\n";
  json += prefix + "  },\n";
  
  json += prefix + "  \"lists\": {\n";    
  json += prefix + "    \"all_lists_count\"      : " + this.all_lists_count + ",\n\n";
  
  json += prefix + "    \"ul_count\"             : " + this.ul_count + ",\n";
  json += prefix + "    \"ol_count\"             : " + this.ol_count + ",\n";
  json += prefix + "    \"li_count\"             : " + this.li_count + ",\n\n";
  
  json += prefix + "    \"dl_count\"             : " + this.dl_count + ",\n";
  json += prefix + "    \"dt_count\"             : " + this.dt_count + ",\n";
  json += prefix + "    \"dd_count\"             : " + this.dd_count + "\n";
  json += prefix + "  },\n";
  
  json += prefix + "  \"links\": {\n";    
  json += prefix + "    \"all_links_count\"      : " + this.all_links_count + ",\n\n";
  
  json += prefix + "    \"a_count\"              : " + this.a_count    + ",\n";
  json += prefix + "    \"area_count\"           : " + this.area_count + "\n";
  json += prefix + "  },\n";
  
  json += prefix + "  \"tables\": {\n";    
  json += prefix + "    \"all_tables_count\"        : " + this.all_tables_count        + ",\n\n";
  
  json += prefix + "    \"table_count\"             : " + this.table_count             + ",\n";
  json += prefix + "    \"th_count\"                : " + this.th_count                + ",\n";
  json += prefix + "    \"td_count\"                : " + this.td_count                + ",\n";
  json += prefix + "    \"caption_count\"           : " + this.caption_count           + ",\n";
  json += prefix + "    \"summary_attribute_count\" : " + this.summary_attribute_count + ",\n";
  json += prefix + "    \"scope_attribute_count\"   : " + this.scope_attribute_count   + ",\n";
  json += prefix + "    \"headers_attribute_count\" : " + this.headers_attribute_count + "\n";
  json += prefix + "  },\n";
  
  json += prefix + "  \"forms\": {\n";    
  json += prefix + "    \"all_forms_count\"      : " + this.all_forms_count      + ",\n\n";
  
  json += prefix + "    \"input_button_count\"   : " + this.input_button_count   + ",\n";
  json += prefix + "    \"input_checkbox_count\" : " + this.input_checkbox_count + ",\n";
  json += prefix + "    \"input_color_count\"    : " + this.input_color_count    + ",\n";
  json += prefix + "    \"input_date_count\"     : " + this.input_date_count     + ",\n";
  json += prefix + "    \"input_datetime_count\" : " + this.input_datetime_count + ",\n";
  json += prefix + "    \"input_email_count\"    : " + this.input_email_count    + ",\n";
  json += prefix + "    \"input_file_count\"     : " + this.input_file_count     + ",\n";
  json += prefix + "    \"input_image_count\"    : " + this.input_image_count    + ",\n";
  json += prefix + "    \"input_month_count\"    : " + this.input_month_count    + ",\n";
  json += prefix + "    \"input_number_count\"   : " + this.input_number_count   + ",\n";
  json += prefix + "    \"input_password_count\" : " + this.input_password_count + ",\n";
  json += prefix + "    \"input_radio_count\"    : " + this.input_radio_count    + ",\n";
  json += prefix + "    \"input_range_count\"    : " + this.input_range_count    + ",\n";
  json += prefix + "    \"input_reset_count\"    : " + this.input_reset_count    + ",\n";
  json += prefix + "    \"input_search_count\"   : " + this.input_search_count   + ",\n";
  json += prefix + "    \"input_submit_count\"   : " + this.input_submit_count   + ",\n";
  json += prefix + "    \"input_tel_count\"      : " + this.input_tel_count      + ",\n";
  json += prefix + "    \"input_text_count\"     : " + this.input_text_count     + ",\n";
  json += prefix + "    \"input_time_count\"     : " + this.input_time_count     + ",\n";
  json += prefix + "    \"input_url_count\"      : " + this.input_url_count      + ",\n";
  json += prefix + "    \"input_week_count\"     : " + this.input_week_count     + ",\n\n";
  
  json += prefix + "    \"autofocus_attribute_count\"   : " + this.autofocus_attribute_count   + ",\n";
  json += prefix + "    \"placeholder_attribute_count\" : " + this.placeholder_attribute_count + ",\n";
  json += prefix + "    \"pattern_attribute_count\"     : " + this.pattern_attribute_count     + ",\n";
  json += prefix + "    \"required_attribute_count\"    : " + this.required_attribute_count    + ",\n\n";

  json += prefix + "    \"output_count\"         : " + this.output_count   + ",\n";
  json += prefix + "    \"progress_count\"       : " + this.progress_count + ",\n";
  json += prefix + "    \"meter_count\"          : " + this.meter_count    + ",\n\n";
  
  json += prefix + "    \"fieldset_count\"        : " + this.fieldset_count        + ",\n";
  json += prefix + "    \"label_count\"           : " + this.label_count           + ",\n";
  json += prefix + "    \"legend_count\"          : " + this.legend_count          + ",\n";
  json += prefix + "    \"title_attribute_count\" : " + this.title_attribute_count + ",\n\n";
  
  json += prefix + "    \"button_count\"         : " + this.button_count   + ",\n";
  json += prefix + "    \"select_count\"         : " + this.select_count   + ",\n";
  json += prefix + "    \"textarea_count\"       : " + this.textarea_count + "\n";
  json += prefix + "  },\n";
  
  json += prefix + "  \"media\": {\n";    
  json += prefix + "    \"all_media_count\"      : " + this.all_media_count + ",\n\n";
  json += prefix + "    \"audio_count\"          : " + this.audio_count     + ",\n";
  json += prefix + "    \"embed_count\"          : " + this.embed_count     + ",\n";
  json += prefix + "    \"object_count\"         : " + this.object_count    + ",\n";
  json += prefix + "    \"video_count\"          : " + this.video_count     + ",\n";
  json += prefix + "    \"source_count\"         : " + this.source_count    + ",\n";
  json += prefix + "    \"track_count\"          : " + this.track_count     + "\n";
  json += prefix + "  },\n";

  json += prefix + "  \"landmarks\": {\n";    
  json += prefix + "    \"all_landmarks_count\"       : " + this.all_landmarks_count      + ",\n\n";
  json += prefix + "    \"role_application_count\"    : " + this.role_application_count   + ",\n";
  json += prefix + "    \"role_banner_count\"         : " + this.role_banner_count        + ",\n";
  json += prefix + "    \"role_complementary_count\"  : " + this.role_complementary_count + ",\n";
  json += prefix + "    \"role_contentinfo_count\"    : " + this.role_contentinfo_count   + ",\n";
  json += prefix + "    \"role_form_count\"           : " + this.role_form_count          + ",\n";
  json += prefix + "    \"role_main_count\"           : " + this.role_main_count          + ",\n";
  json += prefix + "    \"role_navigation_count\"     : " + this.role_navigation_count    + ",\n";
  json += prefix + "    \"role_region_count\"         : " + this.role_region_count        + ",\n";
  json += prefix + "    \"role_search_count\"         : " + this.role_search_count        + "\n";
  json += prefix + "  },\n";

  json += prefix + "  \"structures\": {\n";    
  json += prefix + "    \"all_structures_count\"      : " + this.all_structures_count    + ",\n\n";
  json += prefix + "    \"role_article_count\"        : " + this.role_article_count      + ",\n";
  json += prefix + "    \"role_directory_count\"      : " + this.role_directory_count    + ",\n";
  json += prefix + "    \"role_document_count\"       : " + this.role_document_count     + ",\n";
  json += prefix + "    \"role_heading_count\"        : " + this.role_heading_count      + ",\n";
  json += prefix + "    \"role_math_count\"           : " + this.role_math_count         + ",\n";
  json += prefix + "    \"role_note_count\"           : " + this.role_note_count         + ",\n";
  json += prefix + "    \"role_presentation_count\"   : " + this.role_presentation_count + "\n";
  json += prefix + "  },\n";

  json += prefix + "  \"widgets\": {\n";    
  json += prefix + "    \"all_widgets_count\"           : " + this.all_widgets_count + ",\n\n";
  json += prefix + "    \"role_button_count\"           : " + this.role_button_count + ",\n";
  json += prefix + "    \"role_link_count\"             : " + this.role_link_count + ",\n";
  json += prefix + "    \"aria_describedby_count\"      : " + this.aria_describedby_count + ",\n";
  json += prefix + "    \"aria_describedat_count\"      : " + this.aria_describedat_count + ",\n";
  json += prefix + "    \"aria_labelledby_count\"       : " + this.aria_labelledby_count + ",\n";
  json += prefix + "    \"aria_label_count\"            : " + this.aria_label_count + ",\n";
  json += prefix + "    \"aria_owns_count\"             : " + this.aria_owns_count + ",\n";
  json += prefix + "    \"aria_activedescendant_count\" : " + this.aria_activedescendant_count + ",\n";
  json += prefix + "    \"aria_flowto_count\"           : " + this.aria_flowto_count + ",\n";
  json += prefix + "    \"aria_controls_count\"         : " + this.aria_controls_count + ",\n";
  json += prefix + "    \"aria_hidden_count\"           : " + this.aria_hidden_count + "\n";
  json += prefix + "  },\n";

  json += prefix + "  \"lives\": {\n";    
  json += prefix + "    \"all_lives_count\"       : " + this.all_lives_count   + ",\n\n";
  json += prefix + "    \"aria_live_count\"       : " + this.aria_live_count   + ",\n";
  json += prefix + "    \"role_alert_count\"      : " + this.role_alert_count  + ",\n";
  json += prefix + "    \"role_log_count\"        : " + this.role_log_count    + ",\n";
  json += prefix + "    \"role_status_count\"     : " + this.role_status_count + "\n";
  json += prefix + "  },\n";

  json += prefix + "  \"events\": {\n";    
  json += prefix + "    \"all_events_count\"         : " + this.all_events_count         + ",\n\n";
  json += prefix + "    \"blur_event_count\"         : " + this.blur_event_count         + ",\n";
  json += prefix + "    \"focus_event_count\"        : " + this.focus_event_count        + ",\n";
  json += prefix + "    \"change_event_count\"       : " + this.change_event_count       + ",\n";
  json += prefix + "    \"click_event_count\"        : " + this.click_event_count        + ",\n";
  json += prefix + "    \"double_click_event_count\" : " + this.double_click_event_count + ",\n";
  json += prefix + "    \"drag_events_count\"        : " + this.drag_events_count        + ",\n";
  json += prefix + "    \"keyboard_events_count\"    : " + this.keyboard_events_count    + ",\n";
  json += prefix + "    \"load_event_count\"         : " + this.load_event_count         + ",\n";
  json += prefix + "    \"mouse_events_count\"       : " + this.mouse_events_count       + ",\n";
  json += prefix + "    \"pointer_events_count\"     : " + this.pointer_events_count     + ",\n";
  json += prefix + "    \"touch_events_count\"       : " + this.touch_events_count       + "\n";
  json += prefix + "  }\n";

  if (add_comma) json += prefix + "},\n";
  else json += prefix + "}\n";
  
  return json;
  
};
/*
 * Copyright 2011-2013 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/** 
 * @namespace OpenAjax.a11y.info
 */

OpenAjax.a11y.info = OpenAjax.a11y.info || {};

/* ---------------------------------------------------------------- */
/*                             RuleInfoItem                  */
/* ---------------------------------------------------------------- */
 
 /** 
 * @constructor RuleInfoItem
 *
 * @memberOf OpenAjax.a11y.information
 *
 * @desc Constructor for an object that contains a title, url and description
 *       of some item of information associated with a rule or rule set
 *
 * @param  {Number}  c  - Constant indicating the type of information
 * @param  {String}  t  - Text of the infomration ite, that can be used as 
 *                        the text of a link 
 * @param  {String}  u  - A url to more information on the item   
 * @param  {String}  d  - A longer desctiption of the item
 *
 * @property  {String}  style        - A string that identifies the type of information<br/>
 *                                     The following are the possible values for style:<br/>
 *                                       authoring_tool<br/>
 *                                       example<br/>
 *                                       library_product<br/>
 *                                       manual_check<br/>
 *                                       other<br/>
 *                                       purpose<br/>
 *                                       rule_category<br/>
 *                                       specfication<br/>
 *                                       technique<br/>
 *                                       wcag_technique<br/>
 *        
 * @property  {String}  title        - Text describing the reference, the text 
 *                                     can be used as the text of a link 
 * @property  {String}  url          - A url to more information on the item, if no 
 *                                     this property will be an empty string
 */

OpenAjax.a11y.info.RuleInfoItem = function (c, t, u) {

  var REFS = OpenAjax.a11y.REFERENCES;

  var tc    =  0;
  var style = "";
  var title = "";
  var url   = "";
  
  if (typeof c === 'number') {
    tc = c;
    
    switch (c) {
    
    case REFS.AUTHORING_TOOL:
      style = "authoring_tool";
      break;
      
    case REFS.EXAMPLE:
      style = "example";
      break;
      
    case REFS.LIBRARY_PRODUCT:
      style = "library_product";
      break;
      
    case REFS.MANUAL_CHECK:
      style = "manual_check";
      break;
      
    case REFS.OTHER:
      style = "other";
      break;
      
    case REFS.PURPOSE:
      style = "purpose";
      break;
      
    case REFS.REFERENCE:
      style = "reference";
      break;
      
    case REFS.RULE_CATEGORY:
      style = "rule_category";
      break;
      
    case REFS.SPECIFICATION:
      style = "specfication";
      break;
      
    case REFS.TECHNIQUE:
      style = "technique";
      break;
      
    case REFS.WCAG_TECHNIQUE:
      style = "wcag_technique";
      break;
      
    default:
      break;
    }
  }
  
  if (typeof t === 'string') title = OpenAjax.a11y.util.transformElementMarkup(t);
  if (typeof u === 'string') url = u;
  
  return {
     get style           () { return style; },
     get title           () { return title; },
     get url             () { return url;   },
     toString : function () { return title; }
  };
  
};


/* ---------------------------------------------------------------- */
/*                             RuleResultsGroupInfo                 */
/* ---------------------------------------------------------------- */
 
 /** 
 * @constructor RuleResultsGroupInfo
 *
 * @memberOf OpenAjax.a11y.information
 *
 * @desc Constructor for an object that contains a title, url and description
 *       for a group of rule results
 *
 * @param  {String}  t    - A title that can be used as the text of a link 
 * @param  {String}  u    - A url to more information on the item   
 * @param  {String}  d    - A longer description of the item
 * @param  {Number}  req  - Number of required rules 
 * @param  {Number}  rec  - Number of recommended rules 
 *
 * @property  {String}  title             - A short description that can be used as 
 *                                          a title or the text of a link 
 * @property  {String}  url               - A url to more information on the item   
 * @property  {String}  description       - A longer desctiption of the item
 *
 * @property  {Number}  required_rules    - Number of required rules in the ruleset
 * @property  {Number}  recommended_rules - Number of recommeneded rules in the ruleset
 * @property  {Number}  total_rules       - Total Number of rules in the ruleset
 */

OpenAjax.a11y.info.RuleResultsGroupInfo = function (t, u, d, req, rec) {

  var title  = "";
  var url    = "";
  var desc   = "";
  var r1     = 0;
  var r2     = 0;
  
  if (typeof t === 'string') title = OpenAjax.a11y.util.transformElementMarkup(t);
  if (typeof u === 'string') url   = u;
  if (typeof d === 'string') desc  = OpenAjax.a11y.util.transformElementMarkup(d);
  
  if (typeof req === 'number') r1  = req;
  if (typeof rec === 'number') r2  = rec;
  
  return {
     get title             ()  { return title;   },
     get url               ()  { return url;     },
     get description       ()  { return desc;    },
     get required_rules    ()  { return r1;      },
     get recommended_rules ()  { return r2;      },
     get total_rules       ()  { return r1 + r2; },
     toString : function   ()  { return title;   },
     
     addToRequiredCount    : function (n) { r1 += n; },
     addToRecommendedCount : function (n) { r2 += n; }
  };
  
};

/* ---------------------------------------------------------------- */
/*                                RulesetInfo                       */
/* ---------------------------------------------------------------- */
 
/** 
 * @constructor RulesetInfo
 *
 * @memberOf OpenAjax.a11y.information
 *
 * @desc Constructor for an object that contains a title, url and description
 *       for a group of rule results
 *
 * @param  {String}  i    - ID of the ruleset 
 * @param  {String}  t    - A title that can be used as the text of a link 
 * @param  {String}  u    - A url to more information on the item   
 * @param  {String}  d    - A longer description of the item
 * @param  {Number}  r1   - Number of required rules
 * @param  {Number}  r2   - Number of recommended rules
 * @param  {String}  a1   - Author of ruleset
 * @param  {String}  a2   - Link to more information about the ruleset
 * @param  {String}  l    - Date last updated
 * @param  {String}  v    - Version of ruleset
 *
 * @property  {String}  id                - ID of the ruleset 
 * @property  {String}  title             - A short description that can be used as 
 *                                          a title or the text of a link 
 * @property  {String}  url               - A url to more information on the item   
 * @property  {String}  description       - A longer desctiption of the item
 *
 * @property  {Number}  required_rules    - Number of required rules in the ruleset
 * @property  {Number}  recommended_rules - Number of recommeneded rules in the ruleset
 * @property  {Number}  total_rules       - Total Number of rules in the ruleset
 *
 * @property  {String}  author            - Name of the author of the ruleset
 * @property  {String}  author_url        - URL to more information about the author 
 *                                          of the ruleset
 * @property  {String}  date              - Date the ruleset was last updated
 * @property  {String}  version           - Version of the ruleset
 */

OpenAjax.a11y.info.RulesetInfo = function (i, t, u, d, r1, r2, a1, a2, l, v) {

  var id       = "";
  var title    = "";
  var url      = "";
  var desc     = "";
  var req      = 0;
  var rec      = 0;
  var auth     = "";
  var auth_url = "";
  var date     = "";
  var ver      = "";
  
  if (typeof i  === 'string') id    = i;
  if (typeof t  === 'string') title = OpenAjax.a11y.util.transformElementMarkup(t);
  if (typeof u  === 'string') url   = u;
  if (typeof d  === 'string') desc  = OpenAjax.a11y.util.transformElementMarkup(d);
  
  if (typeof r1 === 'number') req  = r1;
  if (typeof r2 === 'number') rec  = r2;
  
  if (typeof a1 === 'string') auth     = a1;
  if (typeof a2 === 'string') auth_url = a2;

  if (typeof l  === 'string') date  = l;
  if (typeof v  === 'string') ver   = v;

  return {
     get id                () { return id;    },
     get title             () { return title;    },
     get url               () { return url;      },
     get description       () { return desc;     },
     get required_rules    () { return r1;       },
     get recommended_rules () { return r2;       },
     get total_rules       () { return r1 + r2;  },
     get author            () { return auth;     },
     get author_url        () { return auth_url; },
     get date              () { return date;     },
     get version           () { return ver;      },
     toString : function   () { return title;    }
  };
  
};


/* ---------------------------------------------------------------- */
/*                    SuccessCriterionInfo                */
/* ---------------------------------------------------------------- */
 
 /** 
 * @constructor SuccessCriterionInfo
 *
 * @memberOf OpenAjax.a11y.information
 *
 * @desc Constructor for an object that contains information about a WCAG20
 *       Principle, Guideline or Success Criterion
 *
 * @param  {String}  id - ID in the format "P.G.SC" for the WCAG 2.0 item to get information
 *
 * @property  {String}  id           - String id of the principle, guideline or success 
 *                                     criteria (i.e. P.G.SC format)
 * @property  {String}  title        - A title that can be used as the text of a link 
 * @property  {String}  url          - A url to more information on the item   
 * @property  {String}  description  - A longer desctiption of the item
 * @property  {String}  level        - A longer desctiption of the item 
 *                                     (level is only defined for SC, oterwise emtpy)
 */
 
OpenAjax.a11y.info.SuccessCriterionInfo = function (id) {

  var wid   = "";
  var title = "";
  var url   = "";
  var desc  = "";
  var level = "";
  
  var wcag20_nls = OpenAjax.a11y.all_wcag20_nls.getNLS();
  
  var sc = wcag20_nls.getNLSItemById(id);
  
  if (sc && (typeof sc.sc_id === 'string')) {
    wid   = sc.sc_id;
    level = wcag20_nls.levels[sc.level];
    title = sc.title + " (" + level + ")";
    url   = sc.url_spec;
    desc  = sc.description;
  }  
  
  return {
     get id          () { return wid;   },
     get title       () { return title; },
     get url         () { return url;   },
     get description () { return desc;  },  
     get level       () { return level; }, 
     
     toString : function() { return title; }
  };
  
};


/* ---------------------------------------------------------------- */
/*                             ElementSummary                       */
/* ---------------------------------------------------------------- */
 
 /** 
 * @constructor ElementSummary
 *
 * @memberOf OpenAjax.a11y.information
 *
 * @desc Constructor for an object that contains information about the total 
 *       number of elements and specific element types on a web page
 *
 * @param  {Object}  dom_cache    - Cache of the document object model of the web page 
 * @param  {Number}  option       - 0: All elements<br/>
 *                                  1: AT visible elements<br/>
 *                                  2: Hidden elements<br/>
 *
 * @property  {Number}  total       - Total number of elements 
 * @property  {Number}  audio       - Number of audio elements
 * @property  {Number}  controls    - Number of form controls
 * @property  {Number}  headings    - Number of heading elements (h1-h6)
 * @property  {Number}  iframes     - Number of iframes 
 * @property  {Number}  images      - Number of images (img)
 * @property  {Number}  landmarks   - Number of landmarks 
 * @property  {Number}  links       - Number of links (i.e. a and area)
 * @property  {Number}  lists       - Number of lists (i.e. ul, ol and dl)
 * @property  {Number}  listitems   - Number of listitems (i.e. il, dt and dd)
 * @property  {Number}  other       - Number of elements not in one of the 
 *                                    other counts
 * @property  {Number}  tables      - Number of table elements
 * @property  {Number}  table_cells - Number of table cells
 * @property  {Number}  objects     - Number of object, embed or applet elements
 * @property  {Number}  video       - Number of video elements
 * @property  {Number}  widgets     - Number of ARIA identified widgets
 */

OpenAjax.a11y.info.ElementSummary = function (dom_cache, option) {

  function getCount(list) {
  
    var VISIBILITY  = OpenAjax.a11y.VISIBILITY;

    var count = 0;

    if (option === 0) {
      return list.length;
    }
    else {
    
      var list_len = list.length;
  
      for (var i = 0; i < list_len; i++) {
        var de = list[i].dom_element;
        if (!de) de = list[i];
        if (!de) continue;
        
        var cs = de.computed_style;
        
        if (cs) {
          if ((cs.is_visible_to_at === VISIBILITY.VISIBLE) &&
              (option === 1)) count++;
          else if ((cs.is_visible_to_at === VISIBILITY.HIDDEN) &&
              (option === 2)) count++;
        }      
      }       
    }
   
    return count;
  }

  var total       = 0;  
  
  var audio       = 0;
  var controls    = 0;
  var events      = 0;
  var headings    = 0;
  var iframes     = 0;
  var images      = 0;
  var landmarks   = 0;
  var links       = 0;
  var lists       = 0;
  var listitems   = 0;
  var tables      = 0;
  var objects     = 0;
  var video       = 0;
  var widgets     = 0;

  if (dom_cache) {
  
    if (typeof option !== 'number') option = 0; 
  
    audio   = getCount(dom_cache.media_cache.audio_elements);
    video   = getCount(dom_cache.media_cache.video_elements);
    objects = getCount(dom_cache.media_cache.object_elements);

    controls  = getCount(dom_cache.controls_cache.control_elements);
    widgets   = getCount(dom_cache.controls_cache.widget_elements);
    events    = getCount(dom_cache.controls_cache.elements_with_events);
  
    headings  = getCount(dom_cache.headings_landmarks_cache.heading_elements);
    landmarks = getCount(dom_cache.headings_landmarks_cache.landmark_elements);

    iframes   = getCount(dom_cache.headings_landmarks_cache.iframe_elements);
  
    images    = getCount(dom_cache.images_cache.image_elements);

    links     = getCount(dom_cache.links_cache.link_elements);

    lists     = getCount(dom_cache.lists_cache.container_elements);
    listitems = getCount(dom_cache.lists_cache.listitem_elements);

    tables    = getCount(dom_cache.tables_cache.table_elements);

    total     = getCount(dom_cache.element_cache.dom_elements);

  }

  return {
     get audio         () { return audio;       },
     get elements_with_events ()  { return events;  },
     get form_controls () { return controls;    },
     get headings      () { return headings;    },
     get iframes       () { return iframes;     },
     get images        () { return images;      },
     get landmarks     () { return landmarks;   },
     get links         () { return links;       },
     get lists         () { return lists;       },
     get listitems     () { return listitems;   },
     get objects       () { return objects;     },
     get tables        () { return tables;      },
     get total         () { return total;       },
     get video         () { return video;       },
     get widgets       () { return widgets;     }
  };

};

/* ---------------------------------------------------------------- */
/*                             PageInfo                   */
/* ---------------------------------------------------------------- */
 
 /** 
 * @constructor PageInfo
 *
 * @memberOf OpenAjax.a11y.information
 *
 * @desc Constructor for an object that contains a title, url and description
 *       for a group of rule results
 *
 * @param  {Object}  dom_cache    - Cache of the document object model of the web page 
 *
 * @property  {String}  title        - A short description that can be used as 
 *                                     a title or the text of a link 
 * @property  {String}  url          - A url to more information on the item   
 *
 * @property  {ElementSummary}  elements         - Summary information about all 
 *                                                 the elements on the web page
 *
 * @property  {ElementSummary}  visible_elements - Summary information about the 
 *                                                 elements visible to assistive 
 *                                                 technology on the web page
 *
 * @property  {ElementSummary}  hidden_elements - Summary information about the 
 *                                                 elements hidden from assistive 
 *                                                 technology on the web page
 */

OpenAjax.a11y.info.PageInfo = function (dom_cache) {

  var title   = dom_cache.title;
  var url     = dom_cache.url;
  var lang    = "";

  var elements = new OpenAjax.a11y.info.ElementSummary(dom_cache, 0);
  var visible  = new OpenAjax.a11y.info.ElementSummary(dom_cache, 1);
  var hidden   = new OpenAjax.a11y.info.ElementSummary(dom_cache, 2);

  return {
     get title             () { return title;    },
     get url               () { return url;      },
     get language          () { return lang;     },
     get elements          () { return elements;  },
     get visible_elements  () { return visible;  },
     get hidden_elements   () { return hidden;   },
     toString : function   () { return title;    }
  };
  
};




/*
 * Copyright 2011-2013 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


/* ---------------------------------------------------------------- */
/*                             Rule                                 */
/* ---------------------------------------------------------------- */

/**
 * @constructor Rule
 *
 * @memberOf OpenAjax.a11y
 *
 * @desc Creates and validates a rule used to evaluate an accessibility feature 
 *       of a document
 *
 * @param {Object}    rules_nls          - NLS information for rules 
 * @param {Object}    rule_item          - Object containing rule information 
 */ 

/**
 * @private
 * @constructor Internal Properties
 *
 * @property {Object}    cache_nls           - Cache messages NLS reference for the current language  
 * @property {Object}    wcag20_nls          - WCAG 2.0 message NLS reference for the current language  
 * @property {Object}    rules_nls           - Rule message NLS reference for the current language  
 *
 * @property {String}    rule_id             - Unique id of the rule 
 * @property {String}    rule_scope          - Defines the scope of the rule:
 *                                             page or element 
 * @property {Number}    rule_category       - Rule category 
 * @property {String}    wcag_primary_id     - Id of the primary WCAG 2.0 
 *                                             success criterion
 * @property {Array}     wcag_related_ids    - Array of ids of related WCAG 2.0 
 *                                             success criteria
 * @property {String}    cache_dependency    - Which cache (i.e. element group) 
 *                                             the rule will use 
 * @property {Array}     resource_properties - What properties of a cache or dom 
 *                                             element the rules uses in the evaluation
 * @property {Array}     target_objects      - The html objects the rule evaluates 
 *                                             (NOTE: this is informative information)
 * @property {String}    language            - The lanaguage code or codes (space 
 *                                             separated) if the rule is language 
 *                                             specfic, default is empty string
 * @property {function}  validate            - Function for evaluting the rule 
 *                                             requirements using the DOM cache
 *
 * @property {Number}  principle_filter_const          - Number used as a bit 
 *                                                       maskable filter 
 * @property {Number}  guideline_filter_const          - Number used as a bit 
 *                                                       maskable filter    
 * @property {Number}  success_criterion_filter_const  - Number used as a filter
 */

OpenAjax.a11y.Rule = function (rules_nls, rule_item) {

  this.wcag20_nls = OpenAjax.a11y.all_wcag20_nls.getNLS();
  this.rules_nls  = rules_nls[OpenAjax.a11y.locale];
  
  this.rule_id             = rule_item.rule_id;   
  this.rule_scope          = rule_item.rule_scope;   
  this.rule_category       = rule_item.rule_category;   
  this.wcag_primary_id     = rule_item.wcag_primary_id;   
  this.wcag_related_ids    = rule_item.wcag_related_ids;
  this.last_updated        = rule_item.last_updated;
  this.cache_dependency    = rule_item.cache_dependency;
  this.resource_properties = rule_item.resource_properties;
  this.target_resources    = rule_item.target_resources;
  this.language_dependency = rule_item.language_dependency;
  this.validate            = rule_item.validate;
  
  var indexes = this.wcag_primary_id.split('.');

  var p  = parseInt(indexes[0], 10);
  var g  = parseInt(indexes[1], 10);
  var sc = parseInt(indexes[2], 10);
  
  // These are numberical values for fast comparisons
  // There are constants associated with these ids

  // Unique number and bit maskaable
  this.principle_filter_const = Math.pow(2,(p-1)); 
  
  // Unique number and bit maskaable
  this.guideline_filter_const = Math.pow(2, (4*p) + (g-1));    
  
  // Unique number 
  this.success_criterion_filter_const = p * 1000 + g * 100 + sc;
  
};

/**
 * @method getRuleId
 *
 * @memberOf OpenAjax.a11y.Rule
 *
 * @desc Get the programmatic id that uniquely identifies the rule
 *
 * @return {String} The rule id
 */
 
OpenAjax.a11y.Rule.prototype.getRuleId = function () {

  return this.rule_id;
  
};


/**
 * @method getRuleIdNLS
 *
 * @memberOf OpenAjax.a11y.Rule
 *
 * @desc Get a localized human readable id for uniquely identifying the rule
 *
 * @return {String} Localized string of the rule id
 */
 
OpenAjax.a11y.Rule.prototype.getRuleIdNLS = function () {

  return this.rules_nls.rules[this.rule_id]['ID'];
  
};

/**
 * @method getRuleCategoryConstant
 *
 * @memberOf OpenAjax.a11y.Rule
 *
 * @desc Get the bit maskable numeric value for the rule category
 * 
 * @return {Number}  Numeric value of the rule category
 */

OpenAjax.a11y.Rule.prototype.getRuleCategoryConstant = function () {

  return this.rule_category;
  
};

/**
 * @method getRuleCategory
 *
 * @memberOf OpenAjax.a11y.Rule
 *
 * @desc Get a localized title, url and description of the rule category
 * 
 * @return {RuleInfoItem}  Returns a RuleInfoItem object
 */

OpenAjax.a11y.Rule.prototype.getRuleCategory = function () {

  var cache_nls  = OpenAjax.a11y.cache_nls;

  var item = cache_nls.getRuleCategory(this.rule_category);
  
  return (new OpenAjax.a11y.info.RuleInfoItem(this.rule_category, item.title, item.url));
  
};


/**
 * @method getRuleScopeNLS
 *
 * @memberOf OpenAjax.a11y.Rule
 *
 * @desc Get a localized string of the rule scope (i.e. 'element' or 'page')
 *
 * @return {String} Localized string of the rule scope
 */
 
OpenAjax.a11y.Rule.prototype.getRuleScopeNLS = function () {

  if (this.rule_scope) return this.rules_nls.rule_scope[this.rule_scope];
  
  return this.rules_nls.rule_scope[OpenAjax.a11y.RULE_SCOPE.UNKNOWN];
    
};

/**
 * @method getRuleScope
 *
 * @memberOf OpenAjax.a11y.Rule
 *
 * @desc Get the rule scope constant of the rule 
 *
 * @return {Number} rule scope constant
 */
 
OpenAjax.a11y.Rule.prototype.getRuleScope = function () {

  return this.rule_scope;
    
};

/**
 * @method isScopeWebsite
 *
 * @memberOf OpenAjax.a11y.Rule
 *
 * @desc Test if the rule is a website level rule
 *
 * @return {Boolean} True if the rule has a scope of website, otherwise false
 */
 
OpenAjax.a11y.Rule.prototype.isScopeWebsite = function () {

  return this.rule_scope === OpenAjax.a11y.RULE_SCOPE.WEBSITE;
  
};

/**
 * @method isScopePage
 *
 * @memberOf OpenAjax.a11y.Rule
 *
 * @desc Test if the rule is a page level rule
 *
 * @return {Boolean} True if the rule has a scope of page, otherwise false
 */
 
OpenAjax.a11y.Rule.prototype.isScopePage = function () {

  return this.rule_scope === OpenAjax.a11y.RULE_SCOPE.PAGE;
  
};

/**
 * @method isScopeElement
 *
 * @memberOf OpenAjax.a11y.Rule
 *
 * @desc Test if the rule is a element level rule
 *
 * @return {Boolean} True if the rule has a scope of element, otherwise false
 */
 
OpenAjax.a11y.Rule.prototype.isScopeElement = function () {

  return this.rule_scope === OpenAjax.a11y.RULE_SCOPE.ELEMENT;
  
};


/**
 * @method getRuleDefinition
 *
 * @memberOf OpenAjax.a11y.Rule
 *
 * @desc Gets the definition of the rule
 *
 * @param {Boolean}  required  - True if rule is required
 * 
 * @return {String} Localized string of the rule definition based on being 
 *                  required or recommended
 */
OpenAjax.a11y.Rule.prototype.getRuleDefinition = function (required) {

  var str = this.rules_nls.rules[this.rule_id]['DEFINITION'];
  
  var message;
  
  var vstr;

  if (str) {
  
    vstr = "%s";
  
    if (str.indexOf(vstr) >= 0) {

     if (typeof required === 'boolean') {
     
      if (required) message = this.rules_nls.message_severities.MUST;
      else message = this.rules_nls.message_severities.SHOULD;
      
     }
     else {
       // If no rule type is defined assume "must"
        message = this.rules_nls.message_severities.MUST + "/" + this.rules_nls.message_severities.SHOULD;
     }
      
     str = str.replace(vstr, message);  
   }  

   return OpenAjax.a11y.util.transformElementMarkup(str);
 }
      
 return "Definition not found for rule: " + this.rule_id;
  
};

/**
 * @method getRuleSummary
 *
 * @memberOf OpenAjax.a11y.Rule
 *
 * @desc Gets the summary of the rule
 *
 * @param {Boolean}  required  - True if rule is required
 * 
 * @return {String} Localized string of the rule summary based on being 
 *                  required or recommended
 */
OpenAjax.a11y.Rule.prototype.getRuleSummary = function (required) {

  var str = this.rules_nls.rules[this.rule_id]['SUMMARY'];
  
  var message;
  
  var vstr;

  if (str) {
  
    vstr = "%s";
  
    if (str.indexOf(vstr) >= 0) {
    
      if (typeof required === 'boolean') {
     
        if (required) message = this.rules_nls.message_severities.MUST;
        else message = this.rules_nls.message_severities.SHOULD;
      
      }
      else {
        // If no rule type is defined assume "must"
        message = this.rules_nls.message_severities.MUST + "/" + this.rules_nls.message_severities.SHOULD;
      }
      
      str = str.replace(vstr, message);
      
    }  
    return OpenAjax.a11y.util.transformElementMarkup(str);
  }
      
  return "Summary not found for rule: " + this.rule_id;
  
};

/**
 * @method getPurpose
 *
 * @memberOf OpenAjax.a11y.Rule
 *
 * @desc Gets an array strings representing the purpose, basically
 *       how does the rule help people with disabilities
 *
 * @return  {Array}  Returns an array of localized string describing the purpose
 */
OpenAjax.a11y.Rule.prototype.getPurpose = function () {

  var list = this.rules_nls.rules[this.rule_id]['PURPOSE'];
  
  var new_list = [];

  if (list && list.length) { 
  
    for (var i = 0; i < list.length; i++) {

      new_list.push(OpenAjax.a11y.util.transformElementMarkup(list[i]));
    
    } // end for
  
    return new_list;
  }  
  
  return [];
  
};

/**
 * @method getTargetResourcesDescription
 *
 * @memberOf OpenAjax.a11y.Rule
 *
 * @desc Get a description of the markup or page feature the rule is evaluates
 *
 * @return  {String}  Localized string representing the markup or page feature 
 *                    tested by the rule
 */
OpenAjax.a11y.Rule.prototype.getTargetResourcesDescription = function () {

  var target = this.rules_nls.rules[this.rule_id]['TARGET_RESOURCES_DESC'];
  
  if (target) return OpenAjax.a11y.util.transformElementMarkup(target);
  
  return "** Target resource description not defined";
  
};

/**
 * @method getTargetResources
 *
 * @memberOf OpenAjax.a11y.Rule
 *
 * @desc Returns an localized array strings representing target resources of 
 *       the rule
 *
 * @return  {Array}  Returns an array of strings identifying the elements and/or
 *                    attributes that the rule evaluates
 */
OpenAjax.a11y.Rule.prototype.getTargetResources = function () {
  
  if (this.target_resources) return this.target_resources;
  
  return [];
  
};

/**
 * @method getTargetResourceProperties
 *
 * @memberOf OpenAjax.a11y.Rule
 *
 * @desc Get the attributes and calculated properties of element used to evaluate a rule
 *
 * @return  {Array}  Returns an array of strings identifying the calculated properties 
 *                   and/or attributes that the rule uses to evaluate the rule requirements
 */
OpenAjax.a11y.Rule.prototype.getTargetResourceProperties = function () {
  
  if (this.resource_properties) return this.resource_properties;
  
  return [];
  
};

/**
 * @method getTechniques
 *
 * @memberOf OpenAjax.a11y.Rule
 *
 * @desc Get the techniques to implement the requirements of the rule 
 *
 * @return  {Array}  Array of RuleInfoItem objects
 */
OpenAjax.a11y.Rule.prototype.getTechniques = function () {

  var list = this.rules_nls.rules[this.rule_id]['TECHNIQUES'];
  
  var new_list = [];

  if (list && list.length) { 
  
    for (var i = 0; i < list.length; i++) {
    
      var item = list[i];
    
      var ref;
    
      if (typeof item === 'string') ref = new OpenAjax.a11y.info.RuleInfoItem(OpenAjax.a11y.REFERENCES.TECHNIQUE, item, "");
      else ref = new OpenAjax.a11y.info.RuleInfoItem(OpenAjax.a11y.REFERENCES.TECHNIQUE, item.title, item.url);

      new_list.push(ref);
    
    } // end for
  
    return new_list;
  }    
  
  return [];
  
};

/**
 * @method getManualCheckProcedures
 *
 * @memberOf OpenAjax.a11y.Rule
 *
 * @desc Gets manual checking proceedures for evaluating the rule
 *       requirements
 *
 * @return  {Array}  Array of RuleInfoItem objects
 */
 
OpenAjax.a11y.Rule.prototype.getManualCheckProcedures = function () {

  var list = this.rules_nls.rules[this.rule_id]['MANUAL_CHECKS'];
  
  var new_list = [];

  if (list && list.length) { 
  
    for (var i = 0; i < list.length; i++) {

      var item = list[i];
    
      var ref;
    
      if (typeof item === 'string') ref = new OpenAjax.a11y.info.RuleInfoItem(OpenAjax.a11y.REFERENCES.MANUAL_CHECK, item, "");
      else ref = new OpenAjax.a11y.info.RuleInfoItem(OpenAjax.a11y.REFERENCES.MANUAL_CHECK, item.title, item.url);

      new_list.push(ref);
    
    } // end for
  
    return new_list;
  }    
  
  return [];
  
};



/**
 * @method getInformationalLinks
 *
 * @memberOf OpenAjax.a11y.Rule
 *
 * @desc Get information links related to understanding or implementation of the rule
 *
 * @return  {Array}  Returns an array of RuleInfoItem objects
 *
 * @example
 *
 * var node_list = [];
 * var info_links = rule.getInformationalLinks();
 * 
 * for(var i = 0; i < info_links.length; i++) {
 *   var info_link = info_links[i];
 *
 *   // Using object properties to create a link element
 *   var node = document.createElement('a');
 *   node.appendChild(document.createTextNode(info_link.title));
 *   node.setAttribute('href',  info_link.url);
 *   node.setAttribute('class', info_link.type_const.toString());
 *
 *   node_list.push(node);
 * }
 */
 
OpenAjax.a11y.Rule.prototype.getInformationalLinks = function () {

  var list = this.rules_nls.rules[this.rule_id]['INFORMATIONAL_LINKS'];
  
  var new_list = [];

  if (list && list.length) { 
  
    for (var i = 0; i < list.length; i++) {

      var link = list[i];
      
      var ref = new OpenAjax.a11y.info.RuleInfoItem(link.type, link.title, link.url);

      new_list.push(ref);
    
    } // end for
  
    return new_list;
  }  
  
  return [];
  
};



/**
 * @method getPrimarySuccessCriterion
 *
 * @memberOf OpenAjax.a11y.Rule
 *
 * @desc Get information about primary WCAG 2.0 Success Criteria for the rule
 *
 * @return  {SuccessCriterionInfo}  Object representing information about the SC
 *
 * @example
 *
 * var sc_info = rule.getPrimarySuccessCriterion();
 * 
 * // Creating a link element to the primary success criterion
 * var node = document.createElement('a');
 * node.appendChild(document.createTextNode(sc_info.title));
 * node.setAttribute('href',  sc_info.url);
 * node.setAttribute('class', sc_info.style);
 * } 
 */

OpenAjax.a11y.Rule.prototype.getPrimarySuccessCriterion = function () {

  var ref = new OpenAjax.a11y.info.SuccessCriterionInfo(this.wcag_primary_id);

  return ref;

};

/**
 * @method getRelatedSuccessCriteria
 *
 * @memberOf OpenAjax.a11y.Rule
 *
 * @desc Get information about the related WCAG 2.0 Success Criteria for the rule
 *
 * @return  {Array}  Array of SuccessCriterionInfo objects 
 */

OpenAjax.a11y.Rule.prototype.getRelatedSuccessCriteria = function () {

  var list = [];

  var ids = this.wcag_related_ids;
  var ids_len = ids.length;

  for (var i = 0; i < ids_len; i++) {
  
    var id = ids[i];
  
    var ref = new OpenAjax.a11y.info.SuccessCriterionInfo(id);

    list.push(ref);
  }

  return list;
};

/**
 * @method getWCAG20LevelConstant
 *
 * @memberOf OpenAjax.a11y.Rule
 *
 * @desc Get the numerical constant for the WCAG 2.0 Success Criterion Level 
 *       based on the primary id of the rule
 *
 * @return  {Number}  Number representing the WCAG 2.0 level 
 */

OpenAjax.a11y.Rule.prototype.getWCAG20LevelConstant = function () {

  var sc = this.wcag20_nls.getNLSItemById(this.wcag_primary_id);

  return sc.level;

};

/**
 * @method getWCAG20Level
 *
 * @memberOf OpenAjax.a11y.Rule
 *
 * @desc Get the string representation of the the WCAG 2.0 Success Criterion Level 
 *       based on the primary id of the rule
 *
 * @return  {String}  String representing the WCAG 2.0 success criterion level 
 *                    (i.e. A, AA or AAA)
 */

OpenAjax.a11y.Rule.prototype.getWCAG20Level = function () {

  return this.getPrimarySuccessCriterion().level;

};

/**
 * @method getPrincipleFilterConstant
 *
 * @memberOf OpenAjax.a11y.Rule
 *
 * @desc Get a bit maskable filter constant that represents the WCAG 2.0 Principle
 *
 * @return  {Number}  Number representing the WCAG 2.0 Principle
 */

OpenAjax.a11y.Rule.prototype.getPrincipleFilterConstant = function () {

   return this.principle_filter_const;
   
};

/**
 * @method getGuidelineFilterConstant
 *
 * @memberOf OpenAjax.a11y.Rule
 *
 * @desc Get a bit maskable filter constant that represents the WCAG 2.0 Guideline 
 *
 * @return  {Number}  Number representing the WCAG 2.0 Guideline
 */

OpenAjax.a11y.Rule.prototype.getGuidelineFilterConstant = function () {

   return this.guideline_filter_const;
   
};

/**
 * @method getSuccessCriterionFilterConstant
 *
 * @memberOf OpenAjax.a11y.Rule
 *
 * @desc Get a filter constant that represents the WCAG 2.0 Success Criterion 
 *
 * @return  {Number}  Number representing the WCAG 2.0 Success Criterion
 */

OpenAjax.a11y.Rule.prototype.getSuccessCriterionFilterConstant = function () {

   return this.success_criterion_filter_const;
   
};



/**
 * @method toJSON
 *
 * @memberOf OpenAjax.a11y.Rule
 *
 * @desc Returns a JSON representation of the rule
 *
 * @param  {String}   prefix    - String of leading spaces for formatting JSON output (Optional) 
 * @param  {Boolean}  required  - Required is needed for adjusting definition and summary strings to ruleset 
 *                                requirements 
 *
 * @return  {String}  Returns a JSON representation of the rule
 */

OpenAjax.a11y.Rule.prototype.toJSON = function (prefix, required) {

  function stringItem(property, value, last) {
    if (typeof value === 'string') json += prefix + "    \"" + property + "\" : \"" + OpenAjax.a11y.util.escapeForJSON(value)  + "\"";    
    else json += prefix + "    \"" + property + "\" : \"\"";
    
    if (last) json += "\n";
    else json += ",\n";
  }

  function numberItem(property, value, last) {
    json += prefix + "    \"" + property + "\" : " + value;    
    
    if (last) json += "\n";
    else json += ",\n";
  }

  function booleanItem(property, value, last) {
    if (value) json += prefix + "    \"" + property + "\" : true";    
    else json += prefix + "    \"" + property + "\" : false";    
    
    if (last) json += "\n";
    else json += ",\n";
  }

  function stringListItem(property, list, last) {
    json += prefix + "    \"" + property + "\" : [";
    
    if ((typeof list === 'object') && list.length) {    
      var last_item = list.length - 1;    
      for (var i = 0; i < list.length; i++) {
        if (last_item === i) json += "\"" + OpenAjax.a11y.util.escapeForJSON(list[i]) + "\"";
        else json += "\"" + OpenAjax.a11y.util.escapeForJSON(list[i]) + "\",";
      }
    }
    
    if (last) json += "]\n";
    else json += "],\n";
  }

  function stringListItem2(property, list, last ) {
    json += prefix + "    \"" + property + "\" : [";
  
    if ((typeof list === 'object') && list.length) {    
      var last_item = list.length - 1;    
      for (var i = 0; i < list.length; i++) {
        if (last_item === i) json += "  \"" + OpenAjax.a11y.util.escapeForJSON(list[i]) + "\"\n";
        else json += "  \"" + OpenAjax.a11y.util.escapeForJSON(list[i]) + "\",\n";
      }
    }

    if (last) json += "]\n";
    else json += "],\n";

  }


  function addListOfStrings(name, list, last) {

    json += prefix + "    \"" + name + "\" : [\n";
        
    if ((typeof list === 'object') && list.length) {    
      var last_item = list.length - 1;    
      for (var i = 0; i < list.length; i++) {
        if (last_item === i) json += "          \"" + OpenAjax.a11y.util.escapeForJSON(list[i]) + "\"\n";
        else json += "           \"" + OpenAjax.a11y.util.escapeForJSON(list[i]) + "\",\n";
      }
    }

    if (last) json += prefix + "    ]\n";
    else json += prefix + "    ],\n";

  }



  function addInformationalLinks(last) {
  
    function addReferenceItem(reference, last) {

      json += prefix + "      { \"type\"  : "   + reference['type']  + ",\n";
      json += prefix + "        \"title\" : \"" + OpenAjax.a11y.util.escapeForJSON(reference['title']) + "\",\n";
      json += prefix + "        \"url\"   : \"" + OpenAjax.a11y.util.escapeForJSON(reference['url'])   + "\"\n";
    
      if (last) json += prefix + "      }\n";
      else json += prefix + "      },\n";
    
    }
  
    json += prefix + "    \"informational_links\" : [\n";
    
    var info_links = nls_rule['INFORMATIONAL_LINKS'];
    
    if ((typeof info_links === 'object') && info_links.length) {    
      var last_item = info_links.length - 1;    
      for (var i = 0; i < info_links.length; i++) {
        if (last_item === i) addReferenceItem(info_links[i], true);
        else addReferenceItem(info_links[i], false);
      }
    }

    if (last) json += prefix + "    ]\n";
    else json += prefix + "    ],\n";

  }

  function addMessages(name, list, last) {

    json += prefix + "    \"" + name + "\" : {\n";
        
    if (typeof list === 'object') {
      var first = true;    
      for (item in list) {
        if (first) json += "           \"" + OpenAjax.a11y.util.escapeForJSON(item) + "\": \"" + OpenAjax.a11y.util.escapeForJSON(list[item]) + "\"";
        else json += ",\n           \"" + OpenAjax.a11y.util.escapeForJSON(item) + "\": \"" + OpenAjax.a11y.util.escapeForJSON(list[item]) + "\"";
        first = false;
      }
    }
    
    if (last) json += prefix + "\n        }\n";
    else json += prefix + "\n        },\n";

  }




  if (typeof prefix !== 'string') prefix = "";

  var json = "";

  var rule     = this;
  var nls_rule = this.rules_nls.rules[this.rule_id];
  
  OpenAjax.a11y.logger.debug("Exporting rule: " + this.rule_id);
 
  json += prefix + "  {\n";

  stringItem(    'rule_id',             this.rule_id);
  numberItem(    'scope',               this.rule_scope);  
  stringItem(    'wcag_primary',        this.wcag_primary_id);  
  stringListItem('wcag_related',        this.wcag_related_ids);  
  stringItem(    'last_updated',        this.last_updated);
  stringListItem('target_resources',    this.target_resources); 
  numberItem(    'rule_category',       this.rule_category);
  stringItem(    'language_dependency', this.language_dependency);
  stringItem(    'cache_dependency',    this.cache_dependency);
  stringListItem('resource_properties', this.resource_properties); 
  stringItem(    'validate',            this.validate.toString());    
  
  stringItem('nls_rule_id', nls_rule['ID']);
  
  if (typeof required === 'boolean') {
    stringItem('definition', this.getRuleDefinition(required));
    stringItem('summary', this.getRuleSummary(required));
  }
  else {
    stringItem('definition', nls_rule['DEFINITION']);
    stringItem('summary', nls_rule['SUMMARY']);
  }
  
  stringItem('target_resource_desc', nls_rule['TARGET_RESOURCES_DESC']);

  addListOfStrings('purpose',       nls_rule['PURPOSE']);

  addListOfStrings('techniques',    nls_rule['TECHNIQUES']);

  addListOfStrings('manual_checks', nls_rule['MANUAL_CHECKS']);
  
  addInformationalLinks();
  
  addMessages('rule_result_messages', nls_rule['RULE_RESULT_MESSAGES'], false);
  addMessages('node_result_messages', nls_rule['NODE_RESULT_MESSAGES'], true);
  
  json += prefix + "  }";
 
  return json;

};


/* ---------------------------------------------------------------- */
/*                             Rules                                */
/* ---------------------------------------------------------------- */

/**
 * @constructor Rules
 *
 * @memberOf OpenAjax.a11y
 *
 * @desc Creates an array of rule objects for evaluating accessibility
 */

OpenAjax.a11y.Rules = function () {

  this.rules = [];

  this.rules_nls   = {};
  
};

/**
 * @method addRule
 *
 * @memberOf OpenAjax.a11y.Rules
 *
 * @desc Adds a new rule to the list of rules
 *
 * @param  {Object}    rule_item          - Object containing rule information 
 *
 * @return  {Boolean} Returns true if the rule was added successfully; false if there was an error
 */

OpenAjax.a11y.Rules.prototype.addRule = function (rule_item) {

  var errors = false;

  if (typeof rule_item.rule_id !== 'string') {
    OpenAjax.a11y.logger.error("  ** Rule ID is missing");
    errors = true;
  }  

  if (this.getRuleByRuleId(rule_item.rule_id)) {
    OpenAjax.a11y.logger.error("  ** Duplicate Rule ID: " + rule_item.rule_id);
    errors = true;
  }  
  
  if (typeof rule_item.wcag_primary_id !== 'string') {
    OpenAjax.a11y.logger.error("  ** Rule " + rule_item.rule_id + " primary wcag id is missing"); 
    errors = true;
  }  
  
  if (typeof rule_item.wcag_related_ids !== 'object') {
    OpenAjax.a11y.logger.error("  ** Rule " + rule_item.rule_id + " related wcag ids is missing"); 
    errors = true;
  }  
  
  if (!this.validCacheDependency(rule_item.cache_dependency)) {
    OpenAjax.a11y.logger.error("  ** Rule " + rule_item.rule_id + " has invalid or missing cache dependency property"); 
    errors = true;
  }  

  if (typeof rule_item.resource_properties !== 'object') {
    OpenAjax.a11y.logger.error("  ** Rule " + rule_item.rule_id + " resource properties is missing or not an array"); 
    errors = true;
  }  
  
  if (typeof rule_item.language_dependency !== 'string') {
    OpenAjax.a11y.logger.error("  ** Rule " + rule_item.rule_id + " language property is missing or not a string"); 
    errors = true;
  }  
  
  if (typeof rule_item.validate !== 'function') {
    OpenAjax.a11y.logger.error("  ** Rule " + rule_item.rule_id + " validate property is missing or not a function"); 
    errors = true;
  }  

  if (errors) return false;
  
  var oaa_rule = new OpenAjax.a11y.Rule(this.rules_nls, rule_item);

  this.rules.push(oaa_rule);

  return true;

};

/**
 * @method addRulesFromJSON
 *
 * @memberOf OpenAjax.a11y.Rules
 *
 * @desc Adds a rules from a list of rules in JSON format
 *
 * @param {Object}    rule_array  - An array of objects representing OAA rules 
 *
 * @return  {Boolean} Returns true if the rules were added successfully; false if there was an error
 */

OpenAjax.a11y.Rules.prototype.addRulesFromJSON = function (rule_array) {

  var rule_item;

//  OpenAjax.a11y.logger.debug(" ---- Adding OAA Rules ---- ");

  for (var i = 0; i < rule_array.length; i++) {

    rule_item = rule_array[i];
    
    OpenAjax.a11y.logger.debug("  RULE LOADING: " + rule_item.rule_id);
//    OpenAjax.a11y.logger.debug("  last update: " + rule_item.last_updated);
//    OpenAjax.a11y.logger.debug("   dependency: " + rule_item.cache_dependency);
//    OpenAjax.a11y.logger.debug("   properties: " + typeof rule_item.resource_properties);
//    OpenAjax.a11y.logger.debug("     language: " + rule_item.language_dependency);
//    OpenAjax.a11y.logger.debug("     validate: " + typeof rule_item.validate);

    this.addRule(rule_item);

  }

};

/**
 * @method addRulesNLSFromJSON
 *
 * @memberOf OpenAjax.a11y.Rules
 *
 * @desc Adds a rule NLS information for a specific lanaguage
 *
 * @param {String}  locale     - Language of NLS rule information (i.e. 'en-us')
 * @param {Object}  rules_nls  - Object containing rule NLS information 
 */

OpenAjax.a11y.Rules.prototype.addRulesNLSFromJSON = function (locale, rules_nls) {

  this.rules_nls[locale] = this.rules_nls[locale] || {};
  
//  this.rules_nls[locale] = rules_nls; 

  OpenAjax.a11y.logger.debug("  LOADING RULE NLS INFORMATION");

  for (var item in rules_nls) {
  
    switch (item) {

    case 'rule_scope':
    case 'message_severities':
    case 'rule_categories':
    case 'ACTION_NONE':
    case 'NOT_APPLICABLE':
        
      this.rules_nls[locale][item] = rules_nls[item];
      OpenAjax.a11y.logger.debug("  Add " + item + ": " + rules_nls[item]);

      break;
 
    case 'rules':

      this.rules_nls[locale][item] = this.rules_nls[locale][item] || {};

      for (var rule in rules_nls[item]) {
        OpenAjax.a11y.logger.debug("  Add Rule NLS " + rule + ": " + rules_nls[item][rule]);
        this.rules_nls[locale][item][rule] = rules_nls[item][rule];
      }

      break;

    default:
      break;

    }
  }

};

/**
 * @method getRuleByRuleId
 *
 * @memberOf OpenAjax.a11y.Rules
 *
 * @desc Returned rule object with the id 
 *
 * @param  {String}  rule_id   - id of the rule to find 
 *
 * @return  {Rule} Returns rule object if the rule id is found; null if the rule id is not found 
 */

OpenAjax.a11y.Rules.prototype.getRuleByRuleId = function (rule_id) {

  var rule;
  var max = this.rules.length;

  for (var i = 0; i < max; i++ ) {
    rule = this.rules[i];
    if (rule.rule_id === rule_id) return rule;
  }

  return null;
};

/**
 * @method validCacheDependency
 *
 * @memberOf OpenAjax.a11y.Rules
 *
 * @desc Checks to see if the cache reference is valid 
 *
 * @param {String}    cache_name   - Property name of the cache 
 *
 * @return  {Boolean} Returns true if the cache dependency is valid; otherwise false
 */

OpenAjax.a11y.Rules.prototype.validCacheDependency = function (cache_name) {

  var CACHE_NAMES = OpenAjax.a11y.CACHE_NAMES;
  
  var max = CACHE_NAMES.length;
  
  for (var i = 0; i < max; i++) {
    if (cache_name === CACHE_NAMES[i]) return true;
  } // end loop
                    
  return false;
};

/**
 * @method toJSON
 *
 * @memberOf OpenAjax.a11y.Rules
 *
 * @desc Exports current rule information to a JSON format 
 *
 * @return {String}  JSON formatted string
 */

OpenAjax.a11y.Rules.prototype.toJSON = function (prefix) {

  if (typeof prefix !== 'string') prefix = "";

  var json = "";

  json += prefix + "[\n";

  var last = this.rules.length - 1;
  for (var i = 0; i < this.rules.length; i++ ) {
  
    var rule = this.rules[i];
  
    if (i === last) json += rule.toJSON(prefix)  + "\n";
    else json += rule.toJSON(prefix) + ",\n";
    
  }

  json += prefix + "]\n";

  return json;

};
  /*
 * Copyright 2011-2013 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* ---------------------------------------------------------------- */
/*                             NodeResultSummary                        */
/* ---------------------------------------------------------------- */
 
 /** 
 * @constructor NodeResultSummary
 *
 * @memberOf OpenAjax.a11y
 *
 * @desc Constructor for an object that contains summary of node results for rule 
 *       result, cache item result, rule result group result or evaluation result 
 *       objects
 *                                      check 
 * @property  {Number}  percent_passed   - Percentage of node results that pass 
 *                                      the rule (0 <= value <= 100)   
 * @property  {Number}  passed        - Number of node results that passed the 
 *                                      rule (value >= 0)
 * @property  {Number}  page          - Number of node results that contribute to 
 *                                      a page level manual check
 * @property  {Number}  violations    - Number of node results that failed the 
 *                                      rule as a violation (value >= 0) 
 * @property  {Number}  warnings      - Number of node results that failed the 
 *                                      rule as a warning (value >= 0) 
 * @property  {Number}  failures      - Number of node results that failed the 
 *                                      rule as a warning or violation (value >= 0)  
 * @property  {Number}  manual_checks - Number of node results that require a 
 *                                      manual check (value >= 0)  
 * @property  {Number}  hidden        - Number of node results that are hidden
 *                                      (value >= 0)
 * @property  {Number}  total         - Total number of node results that 
 *                                      passed and failed (value >= 0)
 *                                      (note: total count does not include manual 
 *                                             checks)
 */
 
OpenAjax.a11y.NodeResultSummary = function () {

  var pp = 0;
  var p  = 0;  
  var pg = 0;
  var v  = 0;
  var w  = 0;  
  var mc = 0;    
  var h  = 0;    
  var f  = 0;  
  var t  = 0;  

  var has_results = false;
  
  /**
   * @function calcSummary
   * @private
   *
   * @desc Calculates the percentage of passed node results 
   *
   * @param  {Number}  Number of node results that passed
   */

  function calcSummary() {
    if (t !== 0) {
      pp = Math.round((100*p)/t);
      if ((pp > 99) && (f > 0)) pp = 99;    
    }
    
    has_results = (t || mc || h);
    
  }

  return { 
     get percent_passed() { return pp; },
     get passed()         { return p;  },
     get page()           { return pg; },
     get violations()     { return v;  },
     get warnings()       { return w;  },
     get manual_checks()  { return mc; },
     get hidden()         { return h;  },
     get failures ()      { return f;  },
     get total ()         { return t;  },

    /**
     * @method addPassed
     *
     * @memberOf OpenAjax.a11y.NodeResultSummary
     * @private
     *
     * @desc Adds passed node results to the summary calculation 
     *
     * @param  {Number}  n  - Number of node results that passed
     */

     addPassed : function(n) {
     
       if (n > 0) {
         p += n;
         t  += n;
         calcSummary();
       }   
     },


    /**
     * @method addViolations
     * @private
     *
     * @memberOf OpenAjax.a11y.NodeResultSummary
     *
     * @desc Adds violation node results to the summary calculation 
     *
     * @param  {Number}  n  - Number of node results that passed
     */

    addViolations : function(n) {

      if (n > 0) {
        v += n;
        f += n;
        t += n;
        calcSummary();
      }  
    },

    /**
     * @method addWarnings
     * @private
     *
     * @memberOf OpenAjax.a11y.NodeResultSummary
     *
     * @desc Adds warning node results to the summary calculation 
     *
     * @param  {Number}  n  - Number of node results that passed
     */

    addWarnings : function(n) {
    
      if (n > 0) {
        w += n;
        f += n;
        t += n;
        calcSummary();
      }  
    },


    /**
     * @method addManualChecks
     * @private
     *
     * @memberOf OpenAjax.a11y.NodeResultSummary
     *
     * @desc Adds manual check node results to the summary calculation 
     *
     * @param  {Number}  n  - Number of node results that passed
     */

    addManualChecks : function(n) {
      if ( n > 0) {
        mc += n;
        calcSummary();
      }  
    },

    /**
     * @method addHidden
     * @private
     *
     * @memberOf OpenAjax.a11y.NodeResultSummary
     *
     * @desc Adds hidden node results to the summary calculation 
     *
     * @param  {Number}  n  -  Number of node results that are hidden
     */

    addHidden : function(n) {
      if (n > 0) { 
        h += n;
        calcSummary();
      }  
    },


    /**
     * @method addNodeResultSummary
     * @private
     *
     * @memberOf OpenAjax.a11y.NodeResultSummary
     *
     * @desc Adds a result summary to the summary calculation 
     *
     * @param  {NodeResultSummary}  rs node results summary object that passed
     */

    addNodeResultSummary : function(rs) {
    
       p  += rs.passed;
       t  += rs.passed;
       v  += rs.violations;
       t  += rs.violations;
       f  += rs.violations;
       w  += rs.warnings;
       t  += rs.warnings;
       f  += rs.warnings;
       mc += rs.manual_checks;
       h  += rs.hidden;

       calcSummary();
       
    },

    /**
     * @method hasResults
     *
     * @memberOf OpenAjax.a11y.NodeResultSummary
     *
     * @desc Tests the summary results for any node results
     *
     * @return  {Boolean}  If true the summary results have at least one node 
     *                     results, otherwise false
     */
  
    hasResults : function() {
      return has_results;
    },
    
    /**
     * @method toString
     *
     * @memberOf OpenAjax.a11y.NodeResultSummary
     *
     * @desc output information about the summary
     *
     * @return  {String}  Information about node summary
     */
  
    toString : function() {
      return "V: " + v + " W: " + w + " MC: " + mc + " P: " + p + " H: " + h + " Has Results: " + has_results;
    }

  };
};


/* ---------------------------------------------------------------- */
/*                             RuleResultsSummary                        */
/* ---------------------------------------------------------------- */
 
 /** 
 * @constructor RuleResultsSummary
 *
 * @memberOf OpenAjax.a11y
 *
 * @desc Constructor for an object that contains summary of rule results for a 
 *       set of rule result objects or a cache item result
 *
 * @property  {Number}  percent_passed  - Percentage of rule results that all 
 *                                        node results pass
 * @property  {Number}  passed          - Number of rule results that all
 *                                        node results pass
 * @property  {Number}  violations      - Number of rule results with at
 *                                        least one violation 
 * @property  {Number}  warnings        - Number of rule results with at
 *                                        least one warning   
 * @property  {Number}  failures        - Number of rule results with at
 *                                        least one violation or warning    
 * @property  {Number}  manual_checks   - Number of rule results with at
 *                                        least one manual check   
 * @property  {Number}  not_applicable  - Number of rule results with no
 *                                        node results
 * @property  {Number}  total           - Total number of rule results that 
 *                                        with at least on pass, violation
 *                                        warning or manual check
 */
 
OpenAjax.a11y.RuleResultsSummary = function () {

  /**
   * @function calcSummary
   * @private
   *
   * @desc Calculates the percentage of passed node results 
   *
   * @param  {Number}  Number of node results that passed
   */

  function calcSummary() {
    if (t === 0) return;
    pp = Math.round((100*p)/t);
    if ((pp > 99) && (p != t)) pp = 99;
    has_results = true;
  }

  var has_results     = false;
  
  var pp = 0;  
  var p  = 0;  
  var pg = 0;
  var v  = 0;
  var w  = 0;  
  var mc = 0;    
  var na = 0;    
  var f  = 0;  
  var t  = 0;  

  return { 
     get percent_passed() { return pp; },
     get passed()         { return p;  },
     get violations()     { return v;  },
     get warnings()       { return w;  },
     get manual_checks()  { return mc; },
     get failures ()      { return f;  },
     get total ()         { return t;  },
     get not_applicable() { return na;  },

    /**
     * @method addPassed
     *
     * @memberOf OpenAjax.a11y.RuleResultsSummary
     * @private
     *
     * @desc Adds passed rule result to the summary calculation 
     *
     * @param  {RuleResult}  rule_result  - Rule result object to add to summary
     */

     addRuleResult : function(rule_result) {
     
       if (rule_result.node_results_violations.length) {
         v += 1;
         f += 1;
         t += 1;
       }
       else if (rule_result.node_results_warnings.length) {
         w += 1;
         f += 1;       
         t += 1;
       }
       else if (rule_result.node_results_manual_checks.length) {
         mc += 1;
         t += 1;
       }
       else if (rule_result.node_results_passed.length) {
         p += 1;
         t += 1;       
       }
       else {
         na += 1;       
       }

       if (t) calcSummary();

     },

    /**
     * @method addRuleResultsSummary
     * @private
     *
     * @memberOf OpenAjax.a11y.RuleResultsSummary
     *
     * @desc Adds a result summary object to the summary calculation 
     *
     * @param  {ResultSummary}  rs - rule result summary object to add
     */

    addRuleResultsSummary : function(rs) {
    
       v  += rs.violations;
       f  += rs.violations;
       t  += rs.violations;

       w  += rs.warnings;
       f  += rs.warnings;
       t  += rs.warnings;

       mc += rs.manual_checks;
       t += rs.manual_checks;

       p  += rs.passed;
       t  += rs.passed;
       
       na += rs.not_applicable;

       if (t) calcSummary();
       
    },

    /**
     * @method hasResults
     *
     * @memberOf OpenAjax.a11y.RuleResultsSummary
     *
     * @desc Tests the summary results for any rule results
     *
     * @return  {Boolean}  If true the summary results have at least one node 
     *                     results, otherwise false
     */
  
    hasResults : function() {
      return has_results;
    },  
      
    /**
     * @method toString
     *
     * @memberOf OpenAjax.a11y.RuleResultsSummary
     *
     * @desc output information about the summary
     *
     * @return  {String}  Information about rule summary
     */
  
    toString : function() {
      return "V: " + v + " W: " + w + " MC: " + mc + " P: " + p + " NA: " + na + " Has Results: " + has_results;
    }

  };
};







/*
 * Copyright 2011-2013 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


/* ---------------------------------------------------------------- */
/*                       EvaluationResult                           */
/* ---------------------------------------------------------------- */

/**
 * @constructor EvaluationResult
 *
 * @memberOf OpenAjax.a11y
 *
 * @param  {Object}  ruleset                    - Ruleset object used to generate 
 * @param  {String}  title      -  Title of the web page being evaluated 
 * @param  {String}  url        -  URL of the web page being evaluated 
 * @param  {Object}  dom        -  DOM of the web page being evaluated
 * @param  {Object}  dom_cache  -  DOMCache object used for evaluation
 *
 */ 

/**
 * @private
 * @constructor Internal Properties
 *
 * @property  {Object}  ruleset            - Ruleset object used to generate 
 *                                           the evaluation
 * @property  {String}  ruleset_id         - ID of the ruleset
 * @property  {String}  ruleset_title      - Title of the ruleset
 * @property  {String}  ruleset_version    - Version of the ruleset
 * @property  {Number}  evaluation_levels  - WCAG 2.0 evaluations levels used 
 *                                           for evaluation
 *
 * @property  {Boolean} recommended_rules_enabled  - Boolean indicating if recommended 
 *                                                   rules were evaluated
 *
 * @property {String} title  - The title of the document evaluated
 * @property {String} url    - The url of the ldocument evaluated
 * @property {String} date   - Date of the evaluation
 * @property {String} time   - Time of day the document was evaluated
 *
 * @property {Object} doc        - Reference to browser document object model 
 *                                 (DOM) that holds the document to be analyzed
 * @property {Object} dom_cache  - Reference to DOMCache object
 *
 * @property {Array}  rule_results  - Array of rule result objects 
 *
 * @example
 *
 * var url   = window.location.href;
 * var title = window.title;
 * var doc   = window.document;
 *
 * var ruleset            = OpenAjax.a11y.all_rulesets.getRuleset('WCAG20_TRANS');
 * var evaluation_result  = ruleset.evaluate(url, title, doc, null, true);
 */
 
OpenAjax.a11y.EvaluationResult = function (ruleset, title, url, doc, dom_cache) {

  this.ruleset = ruleset;
 
  this.ruleset_id                = ruleset.getRulesetId();
  this.ruleset_title             = ruleset.getRulesetTitle();
  this.ruleset_version           = ruleset.getRulesetVersion();
  this.evaluation_levels         = ruleset.getEvaluationLevelsConstant();
  this.recommended_rules_enabled = ruleset.getRecommendedRulesEnabled();
  
  this.ruleset_description  = ruleset.ruleset_description;
  this.ruleset_author_name  = ruleset.ruleset_author_name;
  this.ruleset_author_url   = ruleset.ruleset_author_url;
  this.required_count       = ruleset.required_count;
  this.recommended_count    = ruleset.recommended_count;
  this.number_of_rules      = ruleset.number_of_rules;
  
  this.title = title;
  this.url   = url;
  
  this.date = OpenAjax.a11y.util.getFormattedDate();
  
  this.doc       = doc;
  this.dom_cache = dom_cache;        
  
  this.rule_results = [];
  
  this.node_result_summary = new OpenAjax.a11y.NodeResultSummary();
  
  this.rule_results_summary = new OpenAjax.a11y.RuleResultsSummary();
  
  this.web_page_information =  new OpenAjax.a11y.info.PageInfo(dom_cache);
  
};

/**
 * @method getDocumentObject
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Get the document object evaluated
 *
 * @return {Object}  Document object
 */
 
OpenAjax.a11y.EvaluationResult.prototype.getDocumentObject = function () {
  return this.doc;
};


/**
 * @method getRulesetInfo
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Return ruleset of information 
 *
 * @return {RulesetInfo}  RulesetInfo object
 */
 
OpenAjax.a11y.EvaluationResult.prototype.getRulesetInfo = function () {
  return this.ruleset.getRulesetInfo();
};

/**
 * @method getPageInfo
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Return information on the web page evaluated 
 *
 * @return {PageInfo}  PageInfo object
 */
 
OpenAjax.a11y.EvaluationResult.prototype.getPageInfo = function () {
  return this.web_page_information;
};


/**
 * @method getTitle
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Get the title of the evaluated document
 *
 * @return {String}  String representing the title
 */
 
OpenAjax.a11y.EvaluationResult.prototype.getTitle = function () {
  return this.title;
};

/**
 * @method getURL
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Get the url of the evaluated document
 *
 * @return {String}  String representing the title
 */
 
OpenAjax.a11y.EvaluationResult.prototype.getURL = function () {
  return this.url;
};

/**
 * @method getDate
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Get the date the document
 *
 * @return {String}  String representing the title
 */
 
OpenAjax.a11y.EvaluationResult.prototype.getDate = function () {
  return this.date;
};




/**
 * @method getRulesetId
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Return id of the ruleset 
 *
 * @return {String}  String representing the ruleset ID
 */
 
OpenAjax.a11y.EvaluationResult.prototype.getRulesetId = function () {
  return this.ruleset_id;
};

/**
 * @method getRulesetTitle
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Return title of the ruleset
 *
 * @return {String}  String representing the NLS title
 */
 
OpenAjax.a11y.EvaluationResult.prototype.getRulesetTitle = function () {
  return this.ruleset_title;
};

/**
 * @method getRulesetVersion
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Return version of the ruleset
 *
 * @return {String}  String representing the version
 */
 
OpenAjax.a11y.EvaluationResult.prototype.getRulesetVersion = function () {
  return this.ruleset_version;
};

/**
 * @method getRulesetDescription
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Return the description of the ruleset
 *
 * @return {String}  String representing the version
 */
 
OpenAjax.a11y.EvaluationResult.prototype.getRulesetDescription = function () {
  return this.ruleset_description;
};

/**
 * @method getRulesetAuthor
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Return the author of the ruleset
 *
 * @return {String}  String representing the author
 */
 
OpenAjax.a11y.EvaluationResult.prototype.getRulesetAuthor = function () {
  return this.ruleset_author_name;
};

/**
 * @method getRulesetAuthorURL
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Return the author url of the ruleset
 *
 * @return {String}  String representing the author url
 */
 
OpenAjax.a11y.EvaluationResult.prototype.getRulesetAuthorURL = function () {
  return this.ruleset_author_url;
};


/**
 * @method getNumberOfRequiredRules
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Return number of required rules
 *
 * @return {Number}  Number of required rules in the ruleset
 */
 
OpenAjax.a11y.EvaluationResult.prototype.getNumberOfRequiredRules = function () {
  return this.required_count;
};

/**
 * @method getNumberOfRecommendedRules
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Return number of recommended rules
 *
 * @return {Number}  Number of recommended rules in the ruleset
 */
 
OpenAjax.a11y.EvaluationResult.prototype.getNumberOfRecommendedRules = function () {
  return this.recommended_count;
};

/**
 * @method getNumberOfRules
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Return total number of rules
 *
 * @return {Number}  Total number of rules in the ruleset
 */
 
OpenAjax.a11y.EvaluationResult.prototype.getNumberOfRules = function () {
  return this.number_of_rules;
};


 /**
 * @method getNodeResultSummary
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Gets numerical summary information about the node results 
 *
 * @return {NodeResultSummary} Returns the NodeResultSummary object 
 */
OpenAjax.a11y.EvaluationResult.prototype.getNodeResultSummary = function () {

  return this.node_result_summary;

};

 /**
 * @method getRuleResultsSummary
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Gets numerical summary information about the rule results 
 *
 * @return {RuleResultsSummary} Returns the rule result summary object 
 */
OpenAjax.a11y.EvaluationResult.prototype.getRuleResultsSummary = function () {

  return this.rule_results_summary;

};

 /**
 * @method getRuleResultsSummary
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Gets numerical summary information about the rule results 
 *
 * @return {RuleResultsSummary} Returns the RuleResultsSummary object 
 */
OpenAjax.a11y.EvaluationResult.prototype.getRuleResultsSummary = function () {

  return this.rule_results_summary;

};

 /**
 * @method getRuleResultById
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Gets rule result object with the associated id
 *
 * @return {RuleResult} Returns the ResultResult object 
 */
OpenAjax.a11y.EvaluationResult.prototype.getRuleResultById = function (id) {

  for (var i = 0; i < this.rule_results.length; i++ ) {
    var rr = this.rule_results[i];

    if (rr.getRuleId() === id) return rr;
  }

  alert("No Rule Result found for: " + id);
   
  return null;

};

/**
 * @method getCacheItemsByElementType
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Returns an object containing a set of cache items based on the element type and evaluation results
 *
 * @param  {Number}  element_type   - Number representing the element type
 * @param  {Number}  filter         - Number representing the evaluation results filter
 *
 * @return {FilteredCacheItemResults}  The object containing the set of cache items
 */

OpenAjax.a11y.EvaluationResult.prototype.getCacheItemsByElementType = function (element_type, filter) {

  var results = new OpenAjax.a11y.FilteredCacheItemResults(this);
  
  if (this.dom_cache) results.getCacheItemResults(element_type, filter);
  
  return results;

};


/**
 * @method getCacheItemsByRuleCategory
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Returns an object containing a set of cache items based on their evaluation results and rule category
 *
 * @param  {Number}  rule_category  - Number representing the rule category
 * @param  {Number}  filter         - Number representing the evaluation results filter
 *
 * @return {FilteredCacheItemResults}  The object containing the set of cache items
 */

OpenAjax.a11y.EvaluationResult.prototype.getCacheItemsByRuleCategory = function (rule_category, filter) {

  var results = new OpenAjax.a11y.FilteredCacheItemResults(this);
  
  if (this.dom_cache) results.getCacheItemResults(rule_category, filter);
  
  return results;

};



/**
 * @method getFilteredRuleResultsByWCAG20
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Returns an object containing a set of rules organized in a tree structure by WCAG 2.0 Principles, Guidelines and Success Criteria
 *
 * @param {Number}  filter        -  Filter for node results (bit mapped mask)
 * 
 * @return {FilteredRuleResultsGroups}  The object containing the filtered rule results organized by wcag 2.0 requirements
 */

OpenAjax.a11y.EvaluationResult.prototype.getFilteredRuleResultsByWCAG20 = function (filter) {

  var RULE_CATEGORIES = OpenAjax.a11y.RULE_CATEGORIES;

  var nls_wcag20 = OpenAjax.a11y.all_wcag20_nls.getNLS();

  var principles;
  var principle;
  var guideline;
  var success_criterion;
  var nls_item;
  
  var evaluation_levels = this.evaluation_levels;

 // OAA_WEB_ACCESSIBILITY_LOGGING.logger.debug("IN WCAG 2.0 SUMMARY");

  principles = new OpenAjax.a11y.FilteredRuleResultsGroups(this, RULE_CATEGORIES.ALL, "WCAG 2.0 Summary");

  nls_item = nls_wcag20.getNLSItemById('1');
  principle = new OpenAjax.a11y.FilteredRuleResultsGroups(this, '1', nls_item.title, nls_item.url_spec, nls_item.description);
  
  nls_item = nls_wcag20.getNLSItemById('1.1');
  guideline = new OpenAjax.a11y.FilteredRuleResultsGroups(this, '1.1', nls_item.title, nls_item.url_spec, nls_item.description);
  
  if (nls_wcag20.getNLSItemById('1.1.1').level & evaluation_levels) {
    nls_item = nls_wcag20.getNLSItemById('1.1.1');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.1.1', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }  
  
  principle.addFilteredRuleResultsGroup(guideline);

  nls_item = nls_wcag20.getNLSItemById('1.2');
  guideline         = new OpenAjax.a11y.FilteredRuleResultsGroups(this, '1.2', nls_item.title, nls_item.url_spec, nls_item.description);
  
  if (nls_wcag20.getNLSItemById('1.2.1').level & evaluation_levels) {
    nls_item = nls_wcag20.getNLSItemById('1.2.1');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.2.1', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('1.2.2').level & evaluation_levels) {
    nls_item = nls_wcag20.getNLSItemById('1.2.2');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.2.2', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('1.2.3').level & evaluation_levels) {  
    nls_item = nls_wcag20.getNLSItemById('1.2.3');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.2.3', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('1.2.4').level & evaluation_levels) {  
    nls_item = nls_wcag20.getNLSItemById('1.2.4');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.2.4', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('1.2.5').level & evaluation_levels) {  
    nls_item = nls_wcag20.getNLSItemById('1.2.5');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.2.5', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('1.2.6').level & evaluation_levels) {  
    nls_item = nls_wcag20.getNLSItemById('1.2.6');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.2.6', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('1.2.7').level & evaluation_levels) {  
    nls_item = nls_wcag20.getNLSItemById('1.2.7');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.2.7', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('1.2.8').level & evaluation_levels) {  
    nls_item = nls_wcag20.getNLSItemById('1.2.8');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.2.8', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('1.2.9').level & evaluation_levels) {  
    nls_item = nls_wcag20.getNLSItemById('1.2.9');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.2.9', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }  
  
  principle.addFilteredRuleResultsGroup(guideline);

  nls_item  = nls_wcag20.getNLSItemById('1.3');
  guideline = new OpenAjax.a11y.FilteredRuleResultsGroups(this, '1.3', nls_item.title, nls_item.url_spec, nls_item.description);
  
  if (nls_wcag20.getNLSItemById('1.3.1').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('1.3.1');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.3.1', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('1.3.2').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('1.3.2');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.3.2', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('1.3.3').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('1.3.3');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.3.3', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  principle.addFilteredRuleResultsGroup(guideline);

  nls_item  = nls_wcag20.getNLSItemById('1.4');
  guideline = new OpenAjax.a11y.FilteredRuleResultsGroups(this, '1.4', nls_item.title, nls_item.url_spec, nls_item.description);
  
  if (nls_wcag20.getNLSItemById('1.4.1').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('1.4.1');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.4.1', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('1.4.2').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('1.4.2');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.4.2', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('1.4.3').level & evaluation_levels) {    
    nls_item  = nls_wcag20.getNLSItemById('1.4.3');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.4.3', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('1.4.4').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('1.4.4');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.4.4', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('1.4.5').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('1.4.5');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.4.5', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('1.4.6').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('1.4.6');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.4.6', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('1.4.7').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('1.4.7');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.4.7', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('1.4.8').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('1.4.8');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.4.8', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('1.4.9').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('1.4.9');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.4.9', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }  
  
  principle.addFilteredRuleResultsGroup(guideline);

  principles.addFilteredRuleResultsGroup(principle);

  nls_item  = nls_wcag20.getNLSItemById('2');
  principle = new OpenAjax.a11y.FilteredRuleResultsGroups(this, '2', nls_item.title, nls_item.url_spec, nls_item.description);
  
  nls_item  = nls_wcag20.getNLSItemById('2.1');
  guideline = new OpenAjax.a11y.FilteredRuleResultsGroups(this, '2.1', nls_item.title, nls_item.url_spec, nls_item.description);
  
  if (nls_wcag20.getNLSItemById('2.1.1').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('2.1.1');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.1.1', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }  
  
  if (nls_wcag20.getNLSItemById('2.1.2').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('2.1.2');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.1.2', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }  
  
  if (nls_wcag20.getNLSItemById('2.1.3').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('2.1.3');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.1.3', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  principle.addFilteredRuleResultsGroup(guideline);

  nls_item  = nls_wcag20.getNLSItemById('2.2');
  guideline = new OpenAjax.a11y.FilteredRuleResultsGroups(this, '2.2', nls_item.title, nls_item.url_spec, nls_item.description);
  
  if (nls_wcag20.getNLSItemById('2.2.1').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('2.2.1');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.2.1', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('2.2.2').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('2.2.2');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.2.2', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('2.2.3').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('2.2.3');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.2.3', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('2.2.4').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('2.2.4');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.2.4', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);    
  }
  
  if (nls_wcag20.getNLSItemById('2.2.5').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('2.2.5');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.2.5', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  principle.addFilteredRuleResultsGroup(guideline);

  nls_item  = nls_wcag20.getNLSItemById('2.3');
  guideline = new OpenAjax.a11y.FilteredRuleResultsGroups(this, '2.3', nls_item.title, nls_item.url_spec, nls_item.description);
  
  if (nls_wcag20.getNLSItemById('2.3.1').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('2.3.1');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.3.1', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('2.3.2').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('2.3.2');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.3.2', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  principle.addFilteredRuleResultsGroup(guideline);

  nls_item  = nls_wcag20.getNLSItemById('2.4');
  guideline = new OpenAjax.a11y.FilteredRuleResultsGroups(this, '2.4', nls_item.title, nls_item.url_spec, nls_item.description);
  
  if (nls_wcag20.getNLSItemById('2.4.1').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('2.4.1');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.4.1', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }  
  
  if (nls_wcag20.getNLSItemById('2.4.2').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('2.4.2');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.4.2', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('2.4.3').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('2.4.3');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.4.3', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }  
  
  if (nls_wcag20.getNLSItemById('2.4.4').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('2.4.4');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.4.4', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }  
  
  if (nls_wcag20.getNLSItemById('2.4.5').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('2.4.5');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.4.5', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }  
  
  if (nls_wcag20.getNLSItemById('2.4.6').level & evaluation_levels) {    
    nls_item  = nls_wcag20.getNLSItemById('2.4.6');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.4.6', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }  
  
  if (nls_wcag20.getNLSItemById('2.4.7').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('2.4.7');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.4.7', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }  
  
  if (nls_wcag20.getNLSItemById('2.4.8').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('2.4.8');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.4.8', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }  
  
  if (nls_wcag20.getNLSItemById('2.4.9').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('2.4.9');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.4.9', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }  
  
  if (nls_wcag20.getNLSItemById('2.4.10').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('2.4.10');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.4.10', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  principle.addFilteredRuleResultsGroup(guideline);

  principles.addFilteredRuleResultsGroup(principle);

  nls_item  = nls_wcag20.getNLSItemById('3');
  principle = new OpenAjax.a11y.FilteredRuleResultsGroups(this, '3', nls_item.title, nls_item.url_spec, nls_item.description);
  
  nls_item  = nls_wcag20.getNLSItemById('3.1');
  guideline = new OpenAjax.a11y.FilteredRuleResultsGroups(this, '3.1', nls_item.title, nls_item.url_spec, nls_item.description);
  
  if (nls_wcag20.getNLSItemById('3.1.1').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('3.1.1');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.1.1', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }  
  
  if (nls_wcag20.getNLSItemById('3.1.2').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('3.1.2');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.1.2', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('3.1.3').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('3.1.3');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.1.3', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }  
  
  if (nls_wcag20.getNLSItemById('3.1.4').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('3.1.4');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.1.4', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }  
  
  if (nls_wcag20.getNLSItemById('3.1.5').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('3.1.5');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.1.5', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);    
  }  
  
  if (nls_wcag20.getNLSItemById('3.1.6').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('3.1.6');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.1.6', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }  
  
  principle.addFilteredRuleResultsGroup(guideline);

  nls_item  = nls_wcag20.getNLSItemById('3.2');
  guideline = new OpenAjax.a11y.FilteredRuleResultsGroups(this, '3.2', nls_item.title, nls_item.url_spec, nls_item.description);
  
  if (nls_wcag20.getNLSItemById('3.2.1').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('3.2.1');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.2.1', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('3.2.2').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('3.2.2');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.2.2', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('3.2.3').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('3.2.3');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.2.3', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('3.2.4').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('3.2.4');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.2.4', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('3.2.5').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('3.2.5');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.2.5', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  principle.addFilteredRuleResultsGroup(guideline);

  nls_item  = nls_wcag20.getNLSItemById('3.3');
  guideline = new OpenAjax.a11y.FilteredRuleResultsGroups(this, '3.3', nls_item.title, nls_item.url_spec, nls_item.description);
  
  if (nls_wcag20.getNLSItemById('3.3.1').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('3.3.1');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.3.1', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('3.3.2').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('3.3.2');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.3.2', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('3.3.3').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('3.3.3');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.3.3', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('3.3.4').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('3.3.4');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.3.4', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('3.3.5').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('3.3.5');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.3.5', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('3.3.6').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('3.3.6');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.3.6', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  principle.addFilteredRuleResultsGroup(guideline);

  principles.addFilteredRuleResultsGroup(principle);

  nls_item  = nls_wcag20.getNLSItemById('4');
  principle = new OpenAjax.a11y.FilteredRuleResultsGroups(this, '4', nls_item.title, nls_item.url_spec, nls_item.description);
  
  nls_item  = nls_wcag20.getNLSItemById('4.1');
  guideline = new OpenAjax.a11y.FilteredRuleResultsGroups(this, '4.1', nls_item.title, nls_item.url_spec, nls_item.description);
  
  if (nls_wcag20.getNLSItemById('4.1.1').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('4.1.1');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '4.1.1', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  if (nls_wcag20.getNLSItemById('4.1.2').level & evaluation_levels) {  
    nls_item  = nls_wcag20.getNLSItemById('4.1.2');
    success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '4.1.2', nls_item.title, nls_item.url_spec, nls_item.description);
    guideline.addFilteredRuleResultsGroup(success_criterion);  
  }
  
  principle.addFilteredRuleResultsGroup(guideline);

  principles.addFilteredRuleResultsGroup(principle);

  for (var i = 0; i < this.rule_results.length; i++) {
  
     var rule_result = this.rule_results[i];
  
     principles.addRuleResult(rule_result.getPrimarySuccessCriterion().id, rule_result, filter);
     
  }   
  
  return principles;


};


/**
 * @method getFilteredRuleResultsByPrinciple
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Returns an object containing a set of rules associated with a WCAG 2.0 Principle
 *
 * @param {Number}  principle_id  -  Number representing the principle id 
 * @param {Number}  filter        -  Filter for node results (bit mapped mask)
 * 
 * @return {FilteredRuleResultsGroups}  The object containing the filtered rule results organized by wcag 2.0 requirements
 */

OpenAjax.a11y.EvaluationResult.prototype.getFilteredRuleResultsByPrinciple = function (principle_id, filter) {

  var nls_wcag20 = OpenAjax.a11y.all_wcag20_nls.getNLS();

  var nls_pr = nls_wcag20.getNLSItemById(principle_id);

  var principle = new OpenAjax.a11y.FilteredRuleResultsGroup(this, principle_id, nls_pr.title, nls_pr.url_spec, nls_pr.description);
    
  for (var i = 0; i < this.rule_results.length; i++) {
  
     var rule_result = this.rule_results[i];
  
     var id = rule_result.getPrincipleFilterConstant();
  
     principle.addRuleResult(id, rule_result, filter);
     
  }   
  
  return principle;

};

/**
 * @method getFilteredRuleResultsByPrinciples
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Returns an object containing a set of rules organized in a tree structure by WCAG 2.0 Principles
 *
 * @param {Number}  filter        -  Filter for node results (bit mapped mask)
 * 
 * @return {FilteredRuleResultsGroups}  The object containing the filtered rule results organized by wcag 2.0 requirements
 */

OpenAjax.a11y.EvaluationResult.prototype.getFilteredRuleResultsByPrinciples = function (filter) {

  var RULE_CATEGORIES  = OpenAjax.a11y.RULE_CATEGORIES;
  var WCAG20_PRINCIPLE = OpenAjax.a11y.WCAG20_PRINCIPLE;

  var nls_wcag20 = OpenAjax.a11y.all_wcag20_nls.getNLS();
 
  var evaluation_levels = this.evaluation_levels;

  var principles = new OpenAjax.a11y.FilteredRuleResultsGroups(this, "wcag20_principles_summary", "WCAG 2.0 Principles");
 
  var nls_pr = nls_wcag20.getNLSItemById('1');
  var principle = new OpenAjax.a11y.FilteredRuleResultsGroup(this, WCAG20_PRINCIPLE.P_1, nls_pr.title, nls_pr.url_spec, nls_pr.description);
  principles.addFilteredRuleResultsGroup(principle);

  nls_pr = nls_wcag20.getNLSItemById('2');
  principle = new OpenAjax.a11y.FilteredRuleResultsGroup(this, WCAG20_PRINCIPLE.P_2, nls_pr.title, nls_pr.url_spec, nls_pr.description);
  principles.addFilteredRuleResultsGroup(principle);  
  
  nls_pr = nls_wcag20.getNLSItemById('3');
  principle = new OpenAjax.a11y.FilteredRuleResultsGroup(this, WCAG20_PRINCIPLE.P_3, nls_pr.title, nls_pr.url_spec, nls_pr.description);
  principles.addFilteredRuleResultsGroup(principle);
  
  nls_pr = nls_wcag20.getNLSItemById('4');
  principle = new OpenAjax.a11y.FilteredRuleResultsGroup(this, WCAG20_PRINCIPLE.P_4, nls_pr.title, nls_pr.url_spec, nls_pr.description);  
  principles.addFilteredRuleResultsGroup(principle);
  
  for (var i = 0; i < this.rule_results.length; i++) {
  
     var rule_result = this.rule_results[i];
  
     var id = rule_result.getPrincipleFilterConstant();
  
     principles.addRuleResult(id, rule_result, filter);
     
  }   
  
  return principles;

};


/**
 * @method getFilteredRuleResultsByGuideline
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Returns an object containing a set of rules associated with a WCAG 2.0 Guideline
 *
 * @param {Number}  guideline_id  -  Number representing the guideline id 
 * @param {Number}  filter        -  Filter for node results (bit mapped mask)
 * 
 * @return {FilteredRuleResultsGroups}  The object containing the filtered rule results organized by wcag 2.0 requirements
 */

OpenAjax.a11y.EvaluationResult.prototype.getFilteredRuleResultsByGuideline = function (guideline_id, filter) {

  var nls_wcag20 = OpenAjax.a11y.all_wcag20_nls.getNLS();

  var nls_gl = nls_wcag20.getNLSItemById(guideline_id);
  var guideline = new OpenAjax.a11y.FilteredRuleResultsGroup(this, guideline_id, nls_gl.title, nls_gl.url_spec, nls_gl.description);
    
  for (var i = 0; i < this.rule_results.length; i++) {
  
     var rule_result = this.rule_results[i];
  
     var id = rule_result.getGuidelineFilterConstant();
     
 //    OpenAjax.a11y.logger.debug(" Rule: " + rule_result.getRuleSummary() + " Rule id " + id  + "  Guideline id: " + guideline_id );     
  
     guideline.addRuleResult(id, rule_result, filter);
     
  }   
  
  return guideline;

};

/**
 * @method getFilteredRuleResultsByGuidelines
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Returns an object containing a set of rules organized in a tree structure by WCAG 2.0 Guidelines
 *
 * @param {Number}  filter        -  Filter for node results (bit mapped mask)
 * 
 * @return {FilteredRuleResultsGroups}  The object containing the filtered rule results organized by wcag 2.0 requirements
 */

OpenAjax.a11y.EvaluationResult.prototype.getFilteredRuleResultsByGuidelines = function (filter) {

  var RULE_CATEGORIES  = OpenAjax.a11y.RULE_CATEGORIES;
  var WCAG20_GUIDELINE = OpenAjax.a11y.WCAG20_GUIDELINE;

  var nls_wcag20 = OpenAjax.a11y.all_wcag20_nls.getNLS();
 
  var guidelines;
  var guideline;
  
  var evaluation_levels = this.evaluation_levels;

  guidelines = new OpenAjax.a11y.FilteredRuleResultsGroups(this, "wcag20_guidelines_summary", "WCAG 2.0 Guidelines");

  var nls_gl = nls_wcag20.getNLSItemById('1.1');
  guideline = new OpenAjax.a11y.FilteredRuleResultsGroup(this, WCAG20_GUIDELINE.G_1_1, nls_gl.title, nls_gl.url_spec, nls_gl.description);
  guidelines.addFilteredRuleResultsGroup(guideline);

  nls_gl = nls_wcag20.getNLSItemById('1.2');
  guideline = new OpenAjax.a11y.FilteredRuleResultsGroup(this, WCAG20_GUIDELINE.G_1_2, nls_gl.title, nls_gl.url_spec, nls_gl.description);
  guidelines.addFilteredRuleResultsGroup(guideline);  
  
  nls_gl = nls_wcag20.getNLSItemById('1.3');
  guideline = new OpenAjax.a11y.FilteredRuleResultsGroup(this, WCAG20_GUIDELINE.G_1_3, nls_gl.title, nls_gl.url_spec, nls_gl.description);
  guidelines.addFilteredRuleResultsGroup(guideline);
  
  nls_gl = nls_wcag20.getNLSItemById('1.4');
  guideline = new OpenAjax.a11y.FilteredRuleResultsGroup(this, WCAG20_GUIDELINE.G_1_4, nls_gl.title, nls_gl.url_spec, nls_gl.description);  
  guidelines.addFilteredRuleResultsGroup(guideline);
  
  nls_gl = nls_wcag20.getNLSItemById('2.1');
  guideline = new OpenAjax.a11y.FilteredRuleResultsGroup(this, WCAG20_GUIDELINE.G_2_1, nls_gl.title, nls_gl.url_spec, nls_gl.description);
  guidelines.addFilteredRuleResultsGroup(guideline);
  
  nls_gl = nls_wcag20.getNLSItemById('2.2');
  guideline = new OpenAjax.a11y.FilteredRuleResultsGroup(this, WCAG20_GUIDELINE.G_2_2, nls_gl.title, nls_gl.url_spec, nls_gl.description);
  guidelines.addFilteredRuleResultsGroup(guideline);
  
  nls_gl = nls_wcag20.getNLSItemById('2.3');
  guideline = new OpenAjax.a11y.FilteredRuleResultsGroup(this, WCAG20_GUIDELINE.G_2_3, nls_gl.title, nls_gl.url_spec, nls_gl.description);  
  guidelines.addFilteredRuleResultsGroup(guideline);
  
  nls_gl = nls_wcag20.getNLSItemById('2.4');
  guideline = new OpenAjax.a11y.FilteredRuleResultsGroup(this, WCAG20_GUIDELINE.G_2_4, nls_gl.title, nls_gl.url_spec, nls_gl.description);  
  guidelines.addFilteredRuleResultsGroup(guideline);

  nls_gl = nls_wcag20.getNLSItemById('3.1');
  guideline = new OpenAjax.a11y.FilteredRuleResultsGroup(this, WCAG20_GUIDELINE.G_3_1, nls_gl.title, nls_gl.url_spec, nls_gl.description);
  guidelines.addFilteredRuleResultsGroup(guideline);
  
  nls_gl = nls_wcag20.getNLSItemById('3.2');
  guideline = new OpenAjax.a11y.FilteredRuleResultsGroup(this, WCAG20_GUIDELINE.G_3_2, nls_gl.title, nls_gl.url_spec, nls_gl.description);
  guidelines.addFilteredRuleResultsGroup(guideline);
  
  nls_gl = nls_wcag20.getNLSItemById('3.3');
  guideline = new OpenAjax.a11y.FilteredRuleResultsGroup(this, WCAG20_GUIDELINE.G_3_3, nls_gl.title, nls_gl.url_spec, nls_gl.description);
  guidelines.addFilteredRuleResultsGroup(guideline);
    
  nls_gl = nls_wcag20.getNLSItemById('4.1');
  guideline = new OpenAjax.a11y.FilteredRuleResultsGroup(this, WCAG20_GUIDELINE.G_4_1, nls_gl.title, nls_gl.url_spec, nls_gl.description);
  guidelines.addFilteredRuleResultsGroup(guideline);

  for (var i = 0; i < this.rule_results.length; i++) {
  
     var rule_result = this.rule_results[i];
  
     var id = rule_result.getGuidelineFilterConstant();
  
     guidelines.addRuleResult(id, rule_result, filter);
     
  }   
  
  return guidelines;

};

/**
 * @method getFilteredRuleResultsBySuccessCriterion
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Returns an object containing a set of rules associated with a WCAG 2.0 Success Criterion
 *
 * @param {Number}  success_criterion_id  -  Number representing the success criterion id 
 * @param {Number}  filter                -  Filter for node results (bit mapped mask)
 * 
 * @return {FilteredRuleResultsGroups}  The object containing the filtered rule results organized by wcag 2.0 requirements
 */

OpenAjax.a11y.EvaluationResult.prototype.getFilteredRuleResultsBySuccessCriterion = function (success_criterion_id, filter) {

  var nls_wcag20 = OpenAjax.a11y.all_wcag20_nls.getNLS();

  var nls_sc = nls_wcag20.getNLSItemById(success_criterion_id);
  var success_criterion = new OpenAjax.a11y.FilteredRuleResultsGroup(this, success_criterion_id.toString(), nls_sc.title, nls_sc.url_spec, nls_sc.description);
    
  for (var i = 0; i < this.rule_results.length; i++) {
  
     var rule_result = this.rule_results[i];

     var id = rule_result.getSuccessCriterionFilterConstant().toString();
     if (typeof success_criterion_id === 'string') id = rule_result.getPrimarySuccessCriterion().id;
  
     success_criterion.addRuleResult(id, rule_result, filter);
     
     
     
  }   
  
  return success_criterion;

};


/**
 * @method getFilteredRuleResultsBySuccessCriteria
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Returns an object containing a set of rules organized in a tree structure 
 *       by WCAG 2.0 Success Criteria
 *
 * @param {Number}  filter        -  Filter for node results (bit mapped mask)
 * 
 * @return {FilteredRuleResultsGroups}  The object containing the filtered rule results organized by wcag 2.0 requirements
 */

OpenAjax.a11y.EvaluationResult.prototype.getFilteredRuleResultsBySuccessCriteria = function (filter) {

  var RULE_CATEGORIES         = OpenAjax.a11y.RULE_CATEGORIES;
  var WCAG20_SUCCESS_CRITERIA = OpenAjax.a11y.WCAG20_SUCCESS_CRITERIA;

  var nls_wcag20 = OpenAjax.a11y.all_wcag20_nls.getNLS();
 
  var success_criteria;
  var sc;
  var nls_sc;
  
  var evaluation_levels = this.evaluation_levels;
  
//  OAA_WEB_ACCESSIBILITY_LOGGING.logger.debug("IN SUCCESS CRITERIA SUMMARY");

  success_criteria = new OpenAjax.a11y.FilteredRuleResultsGroups(this, "wcag20_success_criteria_summary", "WCAG 2.0 Success Criteria");

  if (nls_wcag20.getNLSItemById('1.1.1').level & evaluation_levels) {
    nls_sc = nls_wcag20.getNLSItemById('1.1.1');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.1.1', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }  
    
  if (nls_wcag20.getNLSItemById('1.2.1').level & evaluation_levels) {
    nls_sc = nls_wcag20.getNLSItemById('1.2.1');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.2.1', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('1.2.2').level & evaluation_levels) {
    nls_sc = nls_wcag20.getNLSItemById('1.2.2');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.2.2', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('1.2.3').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('1.2.3');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.2.3', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('1.2.4').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('1.2.4');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.2.4', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('1.2.5').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('1.2.5');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.2.5', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('1.2.6').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('1.2.6');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.2.6', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('1.2.7').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('1.2.7');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.2.7', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('1.2.8').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('1.2.8');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.2.8', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('1.2.9').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('1.2.9');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.2.9', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }  
    
  if (nls_wcag20.getNLSItemById('1.3.1').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('1.3.1');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.3.1', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('1.3.2').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('1.3.2');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.3.2', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('1.3.3').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('1.3.3');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.3.3', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('1.4.1').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('1.4.1');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.4.1', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('1.4.2').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('1.4.2');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.4.2', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('1.4.3').level & evaluation_levels) {    
    nls_sc = nls_wcag20.getNLSItemById('1.4.3');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.4.3', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('1.4.4').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('1.4.4');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.4.4', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('1.4.5').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('1.4.5');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.4.5', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('1.4.6').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('1.4.6');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.4.6', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('1.4.7').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('1.4.7');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.4.7', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('1.4.8').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('1.4.8');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.4.8', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('1.4.9').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('1.4.9');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '1.4.9', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }  
  
  if (nls_wcag20.getNLSItemById('2.1.1').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('2.1.1');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.1.1', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }  
  
  if (nls_wcag20.getNLSItemById('2.1.2').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('2.1.2');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.1.2', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }  
  
  if (nls_wcag20.getNLSItemById('2.1.3').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('2.1.3');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.1.3', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('2.2.1').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('2.2.1');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.2.1', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('2.2.2').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('2.2.2');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.2.2', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('2.2.3').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('2.2.3');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.2.3', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('2.2.4').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('2.2.4');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.2.4', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);    
  }
  
  if (nls_wcag20.getNLSItemById('2.2.5').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('2.2.5');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.2.5', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('2.3.1').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('2.3.1');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.3.1', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('2.3.2').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('2.3.2');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.3.2', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
    
  if (nls_wcag20.getNLSItemById('2.4.1').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('2.4.1');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.4.1', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }  
  
  if (nls_wcag20.getNLSItemById('2.4.2').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('2.4.2');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.4.2', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('2.4.3').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('2.4.3');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.4.3', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }  
  
  if (nls_wcag20.getNLSItemById('2.4.4').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('2.4.4');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.4.4', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }  
  
  if (nls_wcag20.getNLSItemById('2.4.5').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('2.4.5');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.4.5', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }  
  
  if (nls_wcag20.getNLSItemById('2.4.6').level & evaluation_levels) {    
    nls_sc = nls_wcag20.getNLSItemById('2.4.6');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.4.6', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }  
  
  if (nls_wcag20.getNLSItemById('2.4.7').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('2.4.7');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.4.7', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }  
  
  if (nls_wcag20.getNLSItemById('2.4.8').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('2.4.8');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.4.8', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }  
  
  if (nls_wcag20.getNLSItemById('2.4.9').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('2.4.9');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.4.9', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }  
  
  if (nls_wcag20.getNLSItemById('2.4.10').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('2.4.10');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '2.4.10', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
    
  if (nls_wcag20.getNLSItemById('3.1.1').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('3.1.1');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.1.1', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }  
  
  if (nls_wcag20.getNLSItemById('3.1.2').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('3.1.2');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.1.2', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('3.1.3').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('3.1.3');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.1.3', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }  
  
  if (nls_wcag20.getNLSItemById('3.1.4').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('3.1.4');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.1.4', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }  
  
  if (nls_wcag20.getNLSItemById('3.1.5').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('3.1.5');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.1.5', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);    
  }  
  
  if (nls_wcag20.getNLSItemById('3.1.6').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('3.1.6');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.1.6', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }  
  
  
  if (nls_wcag20.getNLSItemById('3.2.1').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('3.2.1');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.2.1', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('3.2.2').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('3.2.2');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.2.2', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('3.2.3').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('3.2.3');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.2.3', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('3.2.4').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('3.2.4');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.2.4', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('3.2.5').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('3.2.5');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.2.5', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
    
  if (nls_wcag20.getNLSItemById('3.3.1').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('3.3.1');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.3.1', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('3.3.2').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('3.3.2');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.3.2', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('3.3.3').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('3.3.3');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.3.3', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('3.3.4').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('3.3.4');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.3.4', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('3.3.5').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('3.3.5');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.3.5', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('3.3.6').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('3.3.6');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '3.3.6', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
    
  if (nls_wcag20.getNLSItemById('4.1.1').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('4.1.1');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '4.1.1', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }
  
  if (nls_wcag20.getNLSItemById('4.1.2').level & evaluation_levels) {  
    nls_sc = nls_wcag20.getNLSItemById('4.1.2');
    sc = new OpenAjax.a11y.FilteredRuleResultsGroup(this, '4.1.2', nls_sc.title, nls_sc.url_spec, nls_sc.description);
    success_criteria.addFilteredRuleResultsGroup(sc);  
  }

  for (var i = 0; i < this.rule_results.length; i++) {
  
     var rule_result = this.rule_results[i];
  
     var id = rule_result.getPrimarySuccessCriterion().id;
     
//     OpenAjax.a11y.logger.debug(" Rule Result ID: " + id);     
 
     success_criteria.addRuleResult(id, rule_result, filter);
     
  }   

  return success_criteria;
  
};


/**
 * @method getFilteredRuleResultsByRuleType
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Returns an object containing a set of rules organized in a tree structure by rule type, defined in a ruleset
 *
 * @param {Number}  filter        -  Filter for node results (bit mapped mask)
 *
 * @return {FilteredRuleResultsGroups}  The object containing the filtered rule results that include tirage rules
 */

OpenAjax.a11y.EvaluationResult.prototype.getFilteredRuleResultsByRuleType = function (filter) {

  var groups = new OpenAjax.a11y.FilteredRuleResultsGroups(this, "", "Rules by Rule Type");
  
  group = new OpenAjax.a11y.FilteredRuleResultsGroup(this, 'true', 'Required Rules');
  groups.addFilteredRuleResultsGroup(group);
  
  group = new OpenAjax.a11y.FilteredRuleResultsGroup(this, 'false', 'Recommended Rules');
  groups.addFilteredRuleResultsGroup(group);
  
  
  for (var i = 0; i < this.rule_results.length; i++) {
  
     var rule_result = this.rule_results[i];
     
     groups.addRuleResult(rule_result.isRuleRequired().toString(), rule_result, filter);
     
  }   

  return groups;

};


/**
 * @method getFilteredRuleResultsByRuleCategories
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Returns an object containing a set of rules organized in a tree structure by rule category
 *
 * @param {Number}  filter        -  Filter for node results (bit mapped mask)
 *
 * @return {FilteredRuleResultsGroups}  The object containing the filtered rule results organized by rule categories
 */

OpenAjax.a11y.EvaluationResult.prototype.getFilteredRuleResultsByRuleCategories = function (filter) {

  var RULE_CATEGORIES = OpenAjax.a11y.RULE_CATEGORIES;

  var groups = new OpenAjax.a11y.FilteredRuleResultsGroups(this, RULE_CATEGORIES.ALL, "Rule Categories");
  var group;

  var nls_cache = OpenAjax.a11y.cache_nls;
  var nls_item;
  
  nls_item = nls_cache.getRuleCategory(RULE_CATEGORIES.AUDIO_VIDEO);
  group = new OpenAjax.a11y.FilteredRuleResultsGroup(this, RULE_CATEGORIES.AUDIO_VIDEO, nls_item.title, nls_item.url, nls_item.desc);
  groups.addFilteredRuleResultsGroup(group);

  nls_item = nls_cache.getRuleCategory(RULE_CATEGORIES.DATA_TABLES);
  group = new OpenAjax.a11y.FilteredRuleResultsGroup(this, RULE_CATEGORIES.DATA_TABLES, nls_item.title, nls_item.url, nls_item.desc);
  groups.addFilteredRuleResultsGroup(group);

  nls_item = nls_cache.getRuleCategory(RULE_CATEGORIES.FORM_CONTROLS);
  group = new OpenAjax.a11y.FilteredRuleResultsGroup(this, RULE_CATEGORIES.FORM_CONTROLS, nls_item.title, nls_item.url, nls_item.desc);
  groups.addFilteredRuleResultsGroup(group);

  nls_item = nls_cache.getRuleCategory(RULE_CATEGORIES.IMAGES);
  group = new OpenAjax.a11y.FilteredRuleResultsGroup(this, RULE_CATEGORIES.IMAGES, nls_item.title, nls_item.url, nls_item.desc);
  groups.addFilteredRuleResultsGroup(group);

  nls_item = nls_cache.getRuleCategory(RULE_CATEGORIES.KEYBOARD_SUPPORT);
  group = new OpenAjax.a11y.FilteredRuleResultsGroup(this, RULE_CATEGORIES.KEYBOARD_SUPPORT, nls_item.title, nls_item.url, nls_item.desc);
  groups.addFilteredRuleResultsGroup(group);

  nls_item = nls_cache.getRuleCategory(RULE_CATEGORIES.LINKS);
  group = new OpenAjax.a11y.FilteredRuleResultsGroup(this, RULE_CATEGORIES.LINKS, nls_item.title, nls_item.url, nls_item.desc);
  groups.addFilteredRuleResultsGroup(group);

  nls_item = nls_cache.getRuleCategory(RULE_CATEGORIES.NAVIGATION_FINDABILITY);
  group = new OpenAjax.a11y.FilteredRuleResultsGroup(this, RULE_CATEGORIES.NAVIGATION_FINDABILITY, nls_item.title, nls_item.url, nls_item.desc);
  groups.addFilteredRuleResultsGroup(group);

  nls_item = nls_cache.getRuleCategory(RULE_CATEGORIES.STRUCTURE_CONTENT);
  group = new OpenAjax.a11y.FilteredRuleResultsGroup(this, RULE_CATEGORIES.STRUCTURE_CONTENT, nls_item.title, nls_item.url, nls_item.desc);
  groups.addFilteredRuleResultsGroup(group);

  nls_item = nls_cache.getRuleCategory(RULE_CATEGORIES.STYLES_READABILITY);
  group = new OpenAjax.a11y.FilteredRuleResultsGroup(this, RULE_CATEGORIES.STYLES_READABILITY, nls_item.title, nls_item.url, nls_item.desc);
  groups.addFilteredRuleResultsGroup(group);

  nls_item = nls_cache.getRuleCategory(RULE_CATEGORIES.WIDGETS_SCRIPTS);
  group = new OpenAjax.a11y.FilteredRuleResultsGroup(this, RULE_CATEGORIES.WIDGETS_SCRIPTS, nls_item.title, nls_item.url, nls_item.desc);
  groups.addFilteredRuleResultsGroup(group);

  for (var i = 0; i < this.rule_results.length; i++) {
  
     var rule_result = this.rule_results[i];
  
     groups.addRuleResult(rule_result.getRuleCategoryConstant(), rule_result, filter);
     
  }   
  
  return groups;

};

/**
 * @method getFilteredRuleResultsByRuleCategory
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Returns an object containing a set of all rule results for a set of rule categories
 *
 * @param {Number}  category      -  Number of bit mask for which rule groups to include in the group 
 * @param {Number}  filter        -  Filter for node results (bit mapped mask)
 *
 * @return {FilteredRuleResultsGroup}  The object containing the filtered rule results
 */

OpenAjax.a11y.EvaluationResult.prototype.getFilteredRuleResultsByRuleCategory = function (category, filter) {

  var nls_item = OpenAjax.a11y.cache_nls.getRuleCategory(category);
  var group = new OpenAjax.a11y.FilteredRuleResultsGroup(this, category, nls_item.title, nls_item.url, nls_item.desc);
    
  for (var i = 0; i < this.rule_results.length; i++) {
  
     var rule_result = this.rule_results[i];
     group.addRuleResult(rule_result.getRuleCategoryConstant(), rule_result, filter);
  }   
  
  return group;

};

/**
 * @method getFilteredRuleResultsByRuleSummary
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Returns an object containing a set of all rule results for a set of rule categories
 *
 * @param {Number}  option        -  Number representing the type of summary 
 * @param {Number}  filter        -  Filter for node results (bit mapped mask)
 *
 * @return {FilteredRuleResultsGroups}  The object containing the filtered rule results
 */

OpenAjax.a11y.EvaluationResult.prototype.getFilteredRuleResultsByRuleSummary = function (option, filter) {

  var RULE_SUMMARY = OpenAjax.a11y.RULE_SUMMARY;

  switch (option) {
  
  case RULE_SUMMARY.CATEGORIES:
    return this.getFilteredRuleResultsByRuleCategories(filter);
    break;
  
  case RULE_SUMMARY.WCAG20:
    return this.getFilteredRuleResultsByWCAG20(filter);
    break;

  case RULE_SUMMARY.PRINCIPLES:
    return this.getFilteredRuleResultsByPrinciples(filter);
    break;

  case RULE_SUMMARY.GUIDELINES:
    return this.getFilteredRuleResultsByGuidelines(filter);
    break;

  case RULE_SUMMARY.SUCCESS_CRITERIA:
    return this.getFilteredRuleResultsBySuccessCriteria(filter);
    break;

  case RULE_SUMMARY.RULESET_TYPE:
    return this.getFilteredRuleResultsByRuleType(filter);
    break;
  

  default:
    break;
  
  }
  
  return null;

};



/**
 * @method isSameDocument
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Determines if a window document property is the one for this evaluation 
 *
 * @param {Object}  document  -  Document Object Model (DOM) to be analyzed
 *
 * @param {Boolean}  True if the same document, false if not
 */
 
OpenAjax.a11y.EvaluationResult.prototype.isSameDocument = function (document) {
    
  return this.doc === document;
    
};
 

/**
 * @method toJSON
 *
 * @memberOf OpenAjax.a11y.EvaluationResult
 *
 * @desc Creates a string representing the evaluation results in a JSON format
 *
 * @param  {Boolean}  node_results_option  -  Optional param, if false then node 
 *                                            results are not included in the JSON file
 *
 * @return {String} Returns a string in JSON format
 */
 
OpenAjax.a11y.EvaluationResult.prototype.toJSON = function (node_results_option) {

  if (typeof node_results_option !== 'boolean') node_results_option = false;
  
  var wcag20_nls  = OpenAjax.a11y.all_wcag20_nls.getNLS();  
  
  var escapeForJSON = OpenAjax.a11y.util.escapeForJSON;
  var cleanForUTF8  = OpenAjax.a11y.util.cleanForUTF8;

  var json = "{\n";

  json += "  \"eval_url\"                  : \"" + escapeForJSON(cleanForUTF8(this.url))   + "\",\n";
  json += "  \"eval_url_encoded\"          : \"" + escapeForJSON(encodeURI(this.url))      + "\",\n";
  json += "  \"eval_title\"                : \"" + escapeForJSON(cleanForUTF8(this.title)) + "\",\n";
  
  json += "  \"ruleset_id\"                : \"" + escapeForJSON(this.ruleset_id)         + "\",\n";
  json += "  \"ruleset_title\"             : \"" + escapeForJSON(this.ruleset_title)      + "\",\n";        
  json += "  \"ruleset_version\"           : \"" + escapeForJSON(this.ruleset_version)    + "\",\n";          
  json += "  \"evaluation_levels_nls\"     : \"" + wcag20_nls.getEvaluationLevelsNLS(this.evaluation_levels) + "\",\n";
  json += "  \"evaluation_levels_code\"    : "   + this.evaluation_levels                                    + ",\n";
  json += "  \"recommended_rules_enabled\" : "   + this.recommended_rules_enabled                            + ",\n";

  json += this.dom_cache.element_information.toJSON(true, "  ");
//  json += this.dom_cache.aria_information.toJSON(true, "  ");
//  json += this.dom_cache.event_information.toJSON(true, "  ");
  
  var rule_results     = this.rule_results;
  var rule_results_len = rule_results.length;

  json += "  \"rule_results\": [\n";

  for (var i = 0; i < rule_results_len; i++) {
    
    var rule_result = rule_results[i];
    var rs = rule_result.getNodeResultSummary();
    
    json += "    {  \"rule_id\"               : \"" + rule_result.getRuleId()                + "\",\n";
    json += "       \"rule_summary\"          : \"" + escapeForJSON(rule_result.getRuleSummary())  + "\",\n";
    json += "       \"rule_category_nls\"     : \"" + rule_result.getRuleCategory()          + "\",\n";
    json += "       \"rule_category_code\"    : "   + rule_result.getRuleCategoryConstant()  + ",\n";
    json += "       \"result_message\"        : \"" + escapeForJSON(rule_result.getResultMessage()) + "\",\n";
    json += "       \"result_value_nls\"      : \"" + rule_result.getRuleResultValueNLS() + "\",\n";
    json += "       \"result_value\"          : "   + rule_result.getRuleResultValue()    + ",\n";
    
    json += "       \"elements_passed\"       : "   + rs.passed             + ",\n";
    json += "       \"elements_violation\"    : "   + rs.violations         + ",\n";
    json += "       \"elements_warning\"      : "   + rs.warnings           + ",\n";
    json += "       \"elements_failure\"      : "   + rs.failures           + ",\n";
    json += "       \"elements_manual_check\" : "   + rs.manual_checks      + ",\n";
    json += "       \"elements_hidden\"       : "   + rs.hidden             + "\n";

    if (node_results_option) {
      json += ",\n";

      json += "       \"node_results\"   : [\n";

      var node_results = rule_result.node_results_violations.concat(rule_result.node_results_warnings, rule_result.node_results_passed, rule_result.node_results_manual_checks, rule_result.node_results_page, rule_result.node_results_hidden);

      for (var j = 0; j < node_results.length; j++ ) {
     
        var node_result = node_results[j];
  
        json += "         {  \"result_value_nls\"  : \"" + node_result.getResultValue().label   + "\",\n";
        json += "            \"result_value_code\" : "   + node_result.getResultValueConstant() + ",\n";
        json += "            \"message\"           : \"" + node_result.getResultMessage()          + "\",\n";
        json += "            \"element\"           : \"" + node_result.cache_item.toString() + "\"\n";
        json += "         }";
       
        if (j < (node_results.length-1)) json += ",\n";
        else json += "\n";
      }
      
      json += "       ]\n";
      
    }  
    else {
      json += "\n";    
    }

    json += "    }";
    
    if (i < (rule_results_len-1))  json += ",\n";
    else json += "\n";

  }

  json += "                ]\n";

  json += "}\n";

  return json;
    
};
 

/*
 * Copyright 2011-2013 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* ---------------------------------------------------------------- */
/*                             RuleResult                           */
/* ---------------------------------------------------------------- */
 
 /** 
 * @constructor RuleResult
 *
 * @memberOf OpenAjax.a11y
 *
 * @desc Constructor for an object that contains a the results of 
 *          the evaluation of a ruleset rule
 *
 * @param  {RuleMapping}  rule_mapping  - RuleMapping object
 */ 

/**
 * @private
 * @constructor Internal Properties
 *
 * @property  {String}   cache_id       - id used to identify the rule result object (uses the same value as the associated rule cache id)
 *
 * @property  {RuleMapping}  rule_mapping    - Reference to the assciated rule
 * @property  {Boolean}            rule_evaluated  - True if rule was evaluated, 
 *                                                   false if rule was disabled or 
 *                                                   not included becase of the WCAG 2.0 
 *                                                   level being evaluated
 *
 * @property  {String}  message          -  String message of rule implementation and correction 
 *
 * @property  {Array}  nodes_passed         - Array of all the node results 
 *                                            that passed
 * @property  {Array}  nodes_violations     - Array of all the node results 
 *                                            that resulted in violations
 * @property  {Array}  nodes_warnings       - Array of all the node results 
 *                                            that resulted in warnings
 * @property  {Array}  nodes_manual_checks  - Array of all the node results 
 *                                            that require manual evaluations
 * @property  {Array}  nodes_page           - Array of all the node results 
 *                                            that are associated with a page result
 * @property  {Array}  nodes_hidden         - Array of all the node results 
 *                                            that are hidden
 *
 * @property  {ResultSummary}  result_summary  - Summary of the node results for 
 *                                               the rule result
 */
 
OpenAjax.a11y.RuleResult = function (rule_mapping) {

  this.rule_mapping = rule_mapping;
  
  this.cache_id        = rule_mapping.rule.rule_id;
  
  this.message = '';

  this.node_results_passed         = [];
  this.node_results_violations     = [];
  this.node_results_warnings       = [];
  this.node_results_manual_checks  = [];
  this.node_results_hidden         = [];
  
  this.node_result_summary = new OpenAjax.a11y.NodeResultSummary();
    
  this.rule_evaluated = false;

};

 /**
 * @method hasResults
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Tests if the rule applied to the content in the page 
 *
 * @return {Boolean} True if the rule has results, otherwise false
 */
 
OpenAjax.a11y.RuleResult.prototype.hasResults = function () {

   return this.node_result_summary.hasResults();

};


 /**
 * @method getNodeResultSummary
 *
 * @memberOf OpenAjax.a11y.RuleRuleResult
 *
 * @desc Gets numerical summary information about the rule results 
 *
 * @return {ResultSummary} Returns the ResultSummary object 
 */
OpenAjax.a11y.RuleResult.prototype.getNodeResultSummary = function () {

  return this.node_result_summary;

};

 /**
 * @method getRuleResult
 * @deprecated
 *
 * @memberOf OpenAjax.a11y.RuleRuleResult
 *
 * @desc Gets the worst node result value from the node results  
 *
 * @return {RESULT_VALUE} Returns a result value constant 
 */
OpenAjax.a11y.RuleResult.prototype.getRuleResult = function () {

  return this.getRuleResultValue();

};


 /**
 * @method getRuleResultValue
 *
 * @memberOf OpenAjax.a11y.RuleRuleResult
 *
 * @desc Gets the worst node result value from the node results  
 *
 * @return {RESULT_VALUE} Returns a result value constant 
 */
OpenAjax.a11y.RuleResult.prototype.getRuleResultValue = function () {

  var RESULT_VALUE = OpenAjax.a11y.RESULT_VALUE;
  var nrs = this.node_result_summary;

  if (nrs.violations)         return RESULT_VALUE.VIOLATION;
  else if (nrs.warnings)      return RESULT_VALUE.WARNING;
  else if (nrs.manual_checks) return RESULT_VALUE.MANUAL_CHECK;
  else if (nrs.passed)        return RESULT_VALUE.PASS;
  else if (nrs.hidden)        return RESULT_VALUE.HIDDEN;
 
  return RESULT_VALUE.NOT_APPLICABLE;

};


 /**
 * @method getRuleResultValueNLS
 *
 * @memberOf OpenAjax.a11y.RuleRuleResult
 *
 * @desc Gets the worst node result value from the node results  
 *
 * @return {RESULT_VALUE} Returns a result value constant 
 */
OpenAjax.a11y.RuleResult.prototype.getRuleResultValueNLS = function () {

  var RESULT_VALUE = OpenAjax.a11y.RESULT_VALUE;
  var nrs = this.node_result_summary;

  if (nrs.violations)         return 'V';
  else if (nrs.warnings)      return 'W';
  else if (nrs.manual_checks) return 'MC';
  else if (nrs.passed)        return 'P';
  else if (nrs.hidden)        return 'H';
 
  return 'n/a';

};
 /**
 * @method getMessage
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Generates a localized rule result message
 *
 * @param {String}  id      -  Id of the rule result message string
 * @param {String}  prefix  -  Prefix message for the string
 *  
 * @return {String} Strings with rule result message
 */
OpenAjax.a11y.RuleResult.prototype.getMessage = function (id, prefix) {

  if (typeof prefix !== 'string') prefix = "";

  var nls_rules = OpenAjax.a11y.all_rules.rules_nls[OpenAjax.a11y.locale];
  
  var rule_id = this.getRuleId();

  var message = nls_rules.rules[rule_id]['RULE_RESULT_MESSAGES'][id];

  if (id === 'ACTION_NONE') { 
    message = nls_rules.ACTION_NONE;
    return message;
  }
  
  if (id === 'NOT_APPLICABLE') { 
    message = nls_rules.NOT_APPLICABLE;
    return message;
  }
  
  if (typeof message !== 'string' || (message.length === 0)) {
    message = "Message is missing for rule id: " + rule_id + " and mesage id: " + id;
  }  
  else {
    message = prefix + message;
  }
  
  var type = "";
  
  if (message.indexOf("%RULE_TYPE") >= 0) {
    
    if (this.rule_mapping.required) type = nls_rules.message_severities.MUST;
    else type = nls_rules.message_severities.SHOULD;

    message = message.replaceAll("%RULE_TYPE", type);  
  }
  
  var rs = this.node_result_summary;
  
  // Replace tokens with rule values

  message = message.replaceAll("%PER",  rs.percent_passed.toString());
  
  message = message.replaceAll("%N_F",  rs.failures.toString());
  
  message = message.replaceAll("%N_P",  rs.passed.toString());
  
  message = message.replaceAll("%N_T",  (rs.total + rs.manual_checks).toString());
  
  message = message.replaceAll("%N_MC", rs.manual_checks.toString());
  
  message = message.replaceAll("%N_H",  rs.hidden.toString());

  message = OpenAjax.a11y.util.transformElementMarkup(message);

  return message;
 
};


 /**
 * @method getResultMessages
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Generates a localized rule result messages 
 *
 * @param {SummaryResult} result_summary  - Optional summary result object to over
 *                                          ride the current result settings
 * 
 * @return {Array} An array of strings with rule result messages (typically only one string in the array)
 */
OpenAjax.a11y.RuleResult.prototype.getResultMessages = function (result_summary) {

  var save_flag = true;  // If data is from this object save
  
  // Test if data object defined and has required properties
  
  var rs = this.node_result_summary;
  if (typeof result_summary === 'object') { 
    rs = result_summary;
    save_flag = false;
  }
  else {
   // if message has already been created return
    if (this.messages && this.messages.length) return this.messages;
  }
  
  var messages = [];
  var m = "";
  
  if ((rs.failures === 0) && (rs.manual_checks === 0)  && (rs.hidden === 0)) {  
  
   if (rs.passed === 0) messages.push(this.getMessage('NOT_APPLICABLE'));
   else messages.push(this.getMessage('ACTION_NONE'));
    
  } 
  else {
  
    if (rs.failures > 0) {
      if (this.isRuleRequired())  m = "V: ";
      else m = "W: ";
      
      if (rs.failures === 1) m += this.getMessage('FAIL_S');
      else m += this.getMessage('FAIL_P');
      messages.push(m);
    }  

    if (rs.manual_checks > 0) {
      if (rs.manual_checks === 1) m = "MC: " + this.getMessage('MANUAL_CHECK_S');
      else m = "MC: " + this.getMessage('MANUAL_CHECK_P');
      messages.push(m);
    }

    if (rs.hidden > 0) {
      if (rs.hidden === 1) m = "H: " + this.getMessage('HIDDEN_S');
      else m = "H: " + this.getMessage('HIDDEN_P');
      messages.push(m);
    }
    

  }

  if (save_flag) this.messages = messages;

  return messages;
  
};

 /**
 * @method getResultMessage
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Generates a localized rule result messages 
 *
 * @param {SummaryResult} result_summary  - Optional summary result object to over
 *                                          ride the current result settings
 * 
 * @return {String} Returns a single string with all result messages
 */
OpenAjax.a11y.RuleResult.prototype.getResultMessage = function (result_summary) {

  var messages = this.getResultMessages(result_summary);
  var last = messages.length - 1;
  var m = "";
  
  for (var i = 0; i < messages.length; i++ ) {
    m += messages[i];
    if (i < last) m += "; ";
  }

  return m;
  
};

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of node results
 */

OpenAjax.a11y.RuleResult.prototype.getNodeResults = function () {
 
  function addResultNodes(items) {
    var i;
    var len = items.length;
    
    for (i = 0; i < len; i++) {
      result_nodes.push(items[i]);
    }    
  }

  var result_nodes = [];
  
  addResultNodes(this.node_results_passed);
  addResultNodes(this.node_results_violations);
  addResultNodes(this.node_results_warnings);
  addResultNodes(this.node_results_manual_checks);
  addResultNodes(this.node_results_hidden); 
  
  return result_nodes;
  
};



/**
 * @method addResult
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Adds a result of an evaluation of rule on a node in the dom  
 *
 * @param  {Number}  test_result         - Number representing if a node passed, failed, manual check or other test result
 * @param  {Object}  cache_item          - Reference to cache item associated with the test
 * @param  {String}  message_id          - Reference to the message string in the NLS file
 * @param  {Array}   message_arguements  - Array of values used in the message string 
 */

OpenAjax.a11y.RuleResult.prototype.addResult = function (test_result, cache_item, message_id, message_arguments, props) {

  var RESULT_VALUE    = OpenAjax.a11y.RESULT_VALUE;
  var TEST_RESULT = OpenAjax.a11y.TEST_RESULT;

  if (!cache_item) return;
  
  var dom_element_item = null; 
 
  if (cache_item.dom_element) {
    dom_element_item = cache_item.dom_element;  
  } 
  else {
    dom_element_item = cache_item;  
  }
  
  dom_element_item.has_rule_results = true;
  
  var node_result_value = RESULT_VALUE.UNKNOWN;
  
  switch (test_result) {
  
  case TEST_RESULT.PASS:
    node_result_value = RESULT_VALUE.PASS;
    break;
    
  case TEST_RESULT.FAIL:
    if (this.rule_mapping.required) node_result_value = RESULT_VALUE.VIOLATION;
    else node_result_value = RESULT_VALUE.WARNING;
    break;
  
  case TEST_RESULT.MANUAL_CHECK:
    node_result_value = RESULT_VALUE.MANUAL_CHECK;
    break;
  
  case TEST_RESULT.HIDDEN:
    node_result_value = RESULT_VALUE.HIDDEN;
    break;
    
  default:
    break;  
  }   
  
  var node_result = new OpenAjax.a11y.NodeResult(this, node_result_value, cache_item, message_id, message_arguments, props);

//  OpenAjax.a11y.logger.debug("  ADD RESULT - text result: " + test_result + " cache item: " + cache_item + "  msg ID: " + message_id + " args: " + message_arguments);

  switch (node_result_value) {
 
  case RESULT_VALUE.HIDDEN: 
    this.node_results_hidden.push(node_result);
    if (dom_element_item)  dom_element_item.rules_hidden.push(node_result);
    this.node_result_summary.addHidden(1);
    break;

  case RESULT_VALUE.PASS:
    this.node_results_passed.push(node_result);
    if (dom_element_item) dom_element_item.rules_passed.push(node_result);
    this.node_result_summary.addPassed(1);
    break;
  
  case RESULT_VALUE.VIOLATION:
    this.node_results_violations.push(node_result);
    if (dom_element_item) dom_element_item.rules_violations.push(node_result);
    this.node_result_summary.addViolations(1);
    break;
  
  case RESULT_VALUE.WARNING:
    this.node_results_warnings.push(node_result);
    if (dom_element_item) dom_element_item.rules_warnings.push(node_result);
    this.node_result_summary.addWarnings(1);
    break;
  
  case RESULT_VALUE.MANUAL_CHECK:
    this.node_results_manual_checks.push(node_result);
    if (dom_element_item) dom_element_item.rules_manual_checks.push(node_result);
    this.node_result_summary.addManualChecks(1);
    break;

  default:
    break; 
  } // end switch 
    
};

/**
 * @method setEvaluationLevelToDisabled
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Sets evaluation level of the rule result to disabled 
 *       (i.e. rule was not evaluated due to user configuration settings)
 */

OpenAjax.a11y.RuleResult.prototype.setEvaluationLevelToDisabled = function () {

  this.is_disabled = true;
  

};  

/**
 * @method isRuleEnabled
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Tests whether the rule is enabled or disabled for evaluation  
 *
 * @param {Boolean}  True if rule is enabled, false if rule disabled
 */

OpenAjax.a11y.RuleResult.prototype.isRuleEnabled = function () {

  return this.rule_mapping.rule.isRuleEnabled();

};

/**
 * @method isRuleRequired
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Tests whether the rule is a required or recommended rule in this ruleset 
 *
 * @param {Boolean}  True if rule is a required rule, false if a recommended rule
 */

OpenAjax.a11y.RuleResult.prototype.isRuleRequired = function () {

  return this.rule_mapping.required;
  
};

/**
 * @method getRuleRequiredYesNo
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Returns 'Yes' or "No' depending on whether the rule is required or recommended rule  
 *
 * @param {String} Returns "Yes" if required, otherwise "No" 
 */

OpenAjax.a11y.RuleResult.prototype.getRuleRequiredYesNo = function () {

  return OpenAjax.a11y.cache_nls.getYesNoNLS(this.isRuleRequired());
  
};

/**
 * @method getRuleRequiredOrRecommended
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Returns 'Required' or "Recommended' depending on whether the rule is required or recommended rule  
 *
 * @param {String} Returns "Required" if required, otherwise "Recommended" 
 */

OpenAjax.a11y.RuleResult.prototype.getRuleRequiredOrRecommended = function () {

  var cache_nls = OpenAjax.a11y.cache_nls.getCacheNLS();
  
  if (this.isRuleRequired()) return cache_nls.required;
  
  return cache_nls.recommended;
  
};



/**
 * @method getRuleId
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Get the programmatic id that uniquely identifies the rule
 *
 * @return {String} The rule id
 */

OpenAjax.a11y.RuleResult.prototype.getRuleId = function () {

  return this.rule_mapping.rule.getRuleId();
   
};

/**
 * @method getRuleIdNLS
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Get a localized human readable id for uniquely identifying the rule
 *
 * @return {String} Localized string of the rule id
 */

OpenAjax.a11y.RuleResult.prototype.getRuleIdNLS = function () {

  return this.rule_mapping.rule.getRuleIdNLS();
   
};

/**
 * @method getRuleCategoryConstant
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Returns the numeric value for the rule category
 * 
 * @return {Number}  Numeric value of the rule category
 */

OpenAjax.a11y.RuleResult.prototype.getRuleCategoryConstant = function () {

  return this.rule_mapping.rule.getRuleCategoryConstant();
  
};

/**
 * @method getRuleCategory
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Get a localized title, url and description of the rule category
 * 
 * @return {Object}  Returns an object with the following propertues:<br/>
 *                   'title':  String representing the Title of the rule category <br/>
 *                   'desc':   String providing a longer description of the rule category<br/>
 *                   'url':    URL to more information about the rule category (maybe blank)<br/>
 */

OpenAjax.a11y.RuleResult.prototype.getRuleCategory = function () {

  return this.rule_mapping.rule.getRuleCategory();
  
};

/**
 * @method getRuleScope
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Get the rule scope constant of the rule 
 *
 * @return {Number} rule scope constant
 */
 
OpenAjax.a11y.RuleResult.prototype.getRuleScope = function () {

  return this.rule_mapping.rule.getRuleScope();
   
};



/**
 * @method getRuleScopeNLS
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Get a localized string of the rule scope (i.e. 'element' or 'page')
 *
 * @return {String} Localized string of the rule scope
 */
 
OpenAjax.a11y.RuleResult.prototype.getRuleScopeNLS = function () {

  return this.rule_mapping.rule.getRuleScopeNLS();
   
};



/**
 * @method isScopeWebsite
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Test if the rule is a website level rule
 *
 * @return {Boolean} True if the rule has a scope of website, otherwise false
 */
 
OpenAjax.a11y.RuleResult.prototype.isScopeWebsite = function () {

  return this.rule_mapping.rule.isScopeWebsite();
  
};

/**
 * @method isScopePage
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Returns an localized string of the rule scope (i.e. element or page)
 *
 * @return {Boolean} True if the rule has a scope of page, otherwise false
 */
 
OpenAjax.a11y.RuleResult.prototype.isScopePage = function () {

  return this.rule_mapping.rule.isScopePage();
  
};



/**
 * @method isScopeElement
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Returns an localized string of the rule scope (i.e. element or page)
 *
 * @return {Boolean} True if the rule has a scope of element, otherwise false
 */
 
OpenAjax.a11y.RuleResult.prototype.isScopeElement = function () {

  return this.rule_mapping.rule.isScopeElement();
  
};





/**
 * @method getRuleDefinition
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Gets the definition of the rule
 *
 * @return {String} Localized string of the rule definition based on being 
 *                  required or recommended
 */
OpenAjax.a11y.RuleResult.prototype.getRuleDefinition = function () {

  return this.rule_mapping.rule.getRuleDefinition(this.rule_mapping.required);
  
};

/**
 * @method getRuleSummary
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Gets the summary of the rule
 *
 * @return {String} Localized string of the rule summary based on being 
 *                  required or recommended
 */

OpenAjax.a11y.RuleResult.prototype.getRuleSummary = function () {

  return this.rule_mapping.rule.getRuleSummary(this.rule_mapping.required);
  
};

/**
 * @method getPurpose
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Gets an array strings representing the purpose, basically
 *       how does the rule help people with disabilities
 *
 * @return  {Array}  Returns an array of localized string describing the purpose
 */
 
OpenAjax.a11y.RuleResult.prototype.getPurpose = function () {

  return this.rule_mapping.rule.getPurpose();
  
};

/**
 * @method getTargetResourcesDescription
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Get a description of the markup or page feature the rule is evaluates
 *
 * @return  {String}  Localized string representing the markup or page feature 
 *                    tested by the rule
 */
OpenAjax.a11y.RuleResult.prototype.getTargetResourcesDescription = function () {

  return this.rule_mapping.rule.getTargetResourcesDescription();

};

/**
 * @method getTargetResources
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Returns an localized array strings representing target resources of 
 *       the rule
 *
 * @return  {Array}  Returns an array of strings identifying the elements and/or
 *                    attributes that the rule evaluates
 */
OpenAjax.a11y.RuleResult.prototype.getTargetResources = function () {

  return this.rule_mapping.rule.getTargetResources();

};

/**
 * @method getTargetResourceProperties
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Get the attributes and properties of element used in evaluating a rule
 *
 * @return  {Array}  Returns an array of strings identifying the elements and/or
 *                    attributes that the rule evaluates
 */
 
OpenAjax.a11y.RuleResult.prototype.getTargetResourceProperties = function () {
  
  return this.rule_mapping.rule.getTargetResourceProperties();
  
};

/**
 * @method getTechniques
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Get the techniques to implement the requirements of the rule 
 *
 * @return  {Array}  Returns an array of objects with localized strings and urls.<br/>
 *                   Each object has the following properties:<br/>
 *                   'title' : Localized string describing the technique<br/> 
 *                   'url': URL to more information about the technique<br/>
 */
 
OpenAjax.a11y.RuleResult.prototype.getTechniques = function () {

  return this.rule_mapping.rule.getTechniques();
  
};

/**
 * @method getManualCheckProcedures
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Gets manual checking proceedures for evaluating the rule
 *       requirements
 *
 * @return  {Array}  Returns an array of objects with localized strings and urls.<br/>
 *                   Each object has the following properties:<br/>
 *                   'title' : Localized string describing the technique<br/> 
 *                   'url': URL to more information about the technique<br/>
 */
OpenAjax.a11y.RuleResult.prototype.getManualCheckProcedures = function () {

  return this.rule_mapping.rule.getManualCheckProcedures();

};

/**
 * @method getInformationalLinks
 *
 * @memberOf OpenAjax.a11y.cache.RuleResult
 *
 * @desc Get information links related to understanding or implementation of the rule
 *
 * @return  {Array}  Returns an array of objects, each object includes the following properties:<br/>
 *                   'type_const' : Number representing the type of information,<br/> 
 *                   'title'      : Title descriping the type of information, <br/>
 *                   'url'        : Link to more information <br/>
 *
 * @example
 *
 * var node_list = [];
 * var info_links = rule.getInformationalLinks();
 * 
 * for(var i = 0; i < info_links.length; i++) {
 *   var info_link = info_links[i];
 *
 *   // Using object properties to create a link element
 *   var node = document.createElement('a');
 *   node.appendChild(document.createTextNode(info_link.title));
 *   node.setAttribute('href',  info_link.url);
 *   node.setAttribute('class', info_link.type_const.toString());
 *
 *   node_list.push(node);
 * }
 */

OpenAjax.a11y.RuleResult.prototype.getInformationalLinks = function () {

  return this.rule_mapping.rule.getInformationalLinks();

};

/**
 * @method getPrimarySuccessCriterion
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Get information about primary WCAG 2.0 Success Criteria for the rule
 *
 * @return  {Object}  Object representing the success criteria, 
 *                    each object has the following properties:<br/> 
 *                    'id'           : A "P.G.SC" formatted string representing the SC,<br/>  
 *                    'title'        : A localized title for the SC,<br/>
 *                    'description'  : A localized description of the SC,<br/>
 *                    'url'          : A url to the SC in the WCAG 2.0 document<br/>
 *                    'level'        : The level of the Success Criterion (e.g. A, AA or AAA)<br/>
 *
 * @example
 *
 * var sc_info = rule.getPrimarySuccessCriterion();
 * 
 * // Creating a link element to the primary success criterion
 * var node = document.createElement('a');
 * node.appendChild(document.createTextNode(sc_info.id + " " + sc_info.title));
 * node.setAttribute('href',  sc_info.url);
 * node.setAttribute('title', sc_info.description);
 * } 
 */

OpenAjax.a11y.RuleResult.prototype.getPrimarySuccessCriterion = function () {

  return this.rule_mapping.rule.getPrimarySuccessCriterion();

};

/**
 * @method getRelatedSuccessCriteria
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Get information about the related WCAG 2.0 Success Criteria for the rule
 *
 * @return  {Array}  Array of objects representing the success criteria, each object has the 
 *                   following properties: <br/>
 *                   'id'           : A "P.G.SC" formatted string representing the SC, <br/> 
 *                   'title'        : A localized title for the SC,<br/>
 *                   'description'  : A localized description of the SC,<br/>
 *                   'url'          : A url to the SC in the WCAG 2.0 document<br/>
 *                   'level'        : The level of the Success Criterion (e.g. A, AA or AAA)<br/>
 */

OpenAjax.a11y.RuleResult.prototype.getRelatedSuccessCriteria = function () {

  return this.rule_mapping.rule.getRelatedSuccessCriteria();

};

/**
 * @method getWCAG20LevelConstant
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Get the numerical constant for the WCAG 2.0 Success Criterion Level 
 *       based on the primary id of the rule
 *
 * @return  {Number}  Number representing the WCAG 2.0 level 
 */

OpenAjax.a11y.RuleResult.prototype.getWCAG20LevelConstant = function () {

  return this.rule_mapping.rule.getWCAG20LevelConstant();

};

/**
 * @method getWCAG20Level
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Get the string representation of the the WCAG 2.0 Success Criterion Level 
 *       based on the primary id of the rule
 *
 * @return  {String}  String representing the WCAG 2.0 success criterion level 
 *                    (i.e. A, AA or AAA)
 */

OpenAjax.a11y.RuleResult.prototype.getWCAG20Level = function () {

  return this.rule_mapping.rule.getWCAG20Level();

};


/**
 * @method getPrincipleFilterConstant
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Get a bit maskable filter constant that represents the WCAG 2.0 Principle
 *
 * @return  {Number}  Number representing the WCAG 2.0 Principle
 */

OpenAjax.a11y.RuleResult.prototype.getPrincipleFilterConstant = function () {

   return this.rule_mapping.rule.getPrincipleFilterConstant();
   
};

/**
 * @method getGuidelineFilterConstant
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Get a bit maskable filter constant that represents the WCAG 2.0 Guideline 
 *
 * @return  {Number}  Number representing the WCAG 2.0 Guideline
 */

OpenAjax.a11y.RuleResult.prototype.getGuidelineFilterConstant = function () {

   return this.rule_mapping.rule.getGuidelineFilterConstant();
   
};

/**
 * @method getSuccessCriterionFilterConstant
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Get a filter constant that represents the WCAG 2.0 Success Criterion 
 *
 * @return  {Number}  Number representing the WCAG 2.0 Success Criterion
 */

OpenAjax.a11y.RuleResult.prototype.getSuccessCriterionFilterConstant = function () {

   return this.rule_mapping.rule.getSuccessCriterionFilterConstant();
   
};


/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.RuleResult
 *
 * @desc Creates a text string representation of the rule result object 
 *
 * @return {String} Returns a text string representation of the rule result object
 */

OpenAjax.a11y.RuleResult.prototype.toString = function () {

 var str = this.getRuleDefinition() + " (";
 
 if (this.node_result_summary.hasResults()) {
   if (this.node_result_summary.passed || this.node_result_summary.failures) str += this.node_result_summary.percent_passed + " percent passed";
   if (this.node_result_summary.manual_checks) str += " " + this.node_result_summary.manual_checks + " manual checks";
 }
 else {
   str += "no results";
 }  

 str += ")"; 

 return str;
 
};

/*
 * Copyright 2011-2013 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


/* ---------------------------------------------------------------- */
/*                             NodeResult                           */
/* ---------------------------------------------------------------- */

/**
 * @constructor NodeResult
 *
 * @memberOf OpenAjax.a11y
 *
 * @desc Constructor for an object that contains a the results of 
 *          the evaluation of a rule on a node
 *
 * @param  {ResultRule}   rule_result             - reference to the rule result object
 * @param  {Number}       result_value            - Constant representing result value of the evaluation result
 * @param  {Object}       cache_item              - Object reference to element information used by this rule result
 * @param  {String}       message_id              - String reference to the message string in the NLS file
 * @param  {Array}        message_arguments       - Array  array of values used in the message string 
 * @param  {Array}        props                   - Array of properties that are defined in the validation function (NOTE: typically undefined)
 */ 

/**
 * @private
 * @constructor Internal Properties
 *
 * @property  {RuleResult} rule_result         - reference to the rule result object
 * @property  {Number}     result_value        - Constant representing result value of the evaluation result
 * @property  {DOMElement} cache_item          - Object reference to cache item associated with the test
 * @property  {String}     message_id          - String reference to the message string in the NLS file
 * @property  {Array}      message_arguments   - Array  array of values used in the message string  
 * @property  {Array}      props               - Array of properties that are defined in the validation function (NOTE: typically undefined)
 */

OpenAjax.a11y.NodeResult = function (rule_result, result_value, cache_item, message_id, message_arguments, props) {

  this.rule_result = rule_result;
  
  this.result_value      = result_value;
  this.cache_item        = cache_item;
  this.message_id        = message_id;
  this.message_arguments = message_arguments;
  this.cache_id          = rule_result.cache_id;
  
//  OpenAjax.a11y.logger.debug("Rule: " + this.getRuleId() + "Prop: " + typeof props);
  
  this.props = [];
  if (typeof props === 'object') this.props = props;
  
};

 /**
 * @method getResultMessage
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Returns an localized node result message 
 *
 * @return {String} String with node result message
 */
OpenAjax.a11y.NodeResult.prototype.getResultMessage = function () {

  var nls_rules = OpenAjax.a11y.all_rules.rules_nls[OpenAjax.a11y.locale];
  
  var RESULT_VALUE = OpenAjax.a11y.RESULT_VALUE;
  
  var message;
  
  // If no message id return the empty string
  if (this.message_id.length === 0) return "";
  
  var rule_id = this.getRuleId();
  
//  OpenAjax.a11y.logger.debug("Rule: " + rule_id);
  
  var str = nls_rules.rules[rule_id]['NODE_RESULT_MESSAGES'][this.message_id];
  
  if (!str) return nls_rules.missing_message + this.message_id;
    
//    OpenAjax.a11y.logger.debug("Rule: " + rule_id + " Message: " + str);

  var vstr; // i.e. %1, %2 ....
  var message_arguments_len = this.message_arguments.length;

  // check to see if message has result value dependence
  
  vstr = "%s";
  
  if (str.indexOf(vstr) >= 0) {
    
    switch (this.result_value) {
    case RESULT_VALUE.VIOLATION:
      message = nls_rules.message_severities.MUST;
      break;

    case RESULT_VALUE.WARNING:
      message = nls_rules.message_severities.SHOULD;
      break;

    case RESULT_VALUE.MANUAL_CHECK:
      message = nls_rules.message_severities.MAY;
      break;

    default:
      message = "";
      break; 
    }

    str = str.replace(vstr, message);  
  }
  
  // Replace 
  
  for (var i = 0; i < message_arguments_len; i++) { 
    vstr = "%" + (i+1); 
    message = this.message_arguments[i];
    
    if (typeof message === 'string') {
      message = message.normalizeSpace();
    }
    else {
      if (typeof message === 'number') {
        message = message.toString();
      }
      else {
        message = "";
      }  
    }  
    str = str.replace(vstr, message);
  } // end loop

  return OpenAjax.a11y.util.transformElementMarkup(str);
  
};

/**
 * @method getTargetResourceProperties
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Get the DOM cache values for the properties associated with a rule
 * 
 * @return {Array} Array of objects with the following properties:<br/>
 *                 'label'       : String label of the property<br/>
 *                 'value'       : String value of the property<br/>
 *                 'description' : String providing additional information about the property <br/>
 */

OpenAjax.a11y.NodeResult.prototype.getTargetResourceProperties = function () {

  var cache_nls = OpenAjax.a11y.cache_nls;

  var nls_prop_list = [];
  
  var prop_list = this.rule_result.getTargetResourceProperties();
  var value;
  var prop_item;
  var nls_item;
  var i;

  for (i = 0; i < prop_list.length; i++) {

    prop_item = prop_list[i];

    value    = this.cache_item.getCachePropertyValue(prop_item);

    nls_item = cache_nls.getLabelAndValueNLS(prop_item, value);

    nls_prop_list.push(nls_item);
    
  }  
  
  for (i = 0; i < this.props.length; i++) {

    nls_prop_list.push(this.props[i]);
    
  }  
  
  
  return nls_prop_list;
   
};



/**
 * @method getResultValue
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Gets the numerical value of the result value of the result 
 *
 * @return {Number} Returns a number representing the result value of the result
 */

OpenAjax.a11y.NodeResult.prototype.getResultValue = function () {

  return this.result_value;
 
};

/**
 * @method getResultValueNLS
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Gets string value for result value abbreviation 
 *
 * @return {Object} Returns a string value of the result value
 */

OpenAjax.a11y.NodeResult.prototype.getResultValueNLS = function () {
 
  var nls =  OpenAjax.a11y.cache_nls.getResultValueNLS(this.result_value);
  
  if (nls) return nls.abbrev;
  
  return "unknown";
 
};


/**
 * @method getResultValueStringsNLS
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Gets result value label, abbreviation, description and style 
 *
 * @return {Object} Returns a object with the following properties: <br/>
 *                  'label'       : String representing the result value <br/>
 *                  'abbrev'      : String representing the abbreviation of the result value<br/>
 *                  'description' : String describing the result value, may be useful as tooltip<br/>
 *                  'style'       : String that can used as a CSS selector for styling results<br/> 
 */

OpenAjax.a11y.NodeResult.prototype.getResultValueStringsNLS = function () {

  return  OpenAjax.a11y.cache_nls.getResultValueNLS(this.result_value);
 
};


/**
 * @method getOrdinalPosition
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Returns a the ordinal position of the element in a list of node results
 * 
 * @return {Number} Returns a number indicating the position in a list of node results
 */

OpenAjax.a11y.NodeResult.prototype.getOrdinalPosition = function () {

  var position = 0;

  if (this.cache_item) {
    if(this.cache_item.dom_element) position = this.cache_item.dom_element.document_order;
    else position = this.cache_item.document_order;
  }
  
  return position;
   
};


/**
 * @method isRuleEnabled
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Tests whether the rule is enabled or disabled for evaluation  
 *
 * @param {Boolean}  True if rule is enabled, false if rule disabled
 */

OpenAjax.a11y.NodeResult.prototype.isRuleEnabled = function () {

  return this.rule_result.isRuleEnabled();

};

/**
 * @method isRuleRequired
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Tests whether the rule is mapped as a required or recommended rule  
 *
 * @param {Boolean}  True if rule is a required rule, false if a recommended rule
 */

OpenAjax.a11y.NodeResult.prototype.isRuleRequired = function () {

  return this.rule_result.isRuleRequired();

};


/**
 * @method getRuleRequiredYesNo
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Returns 'Yes' or "No' depending on whether the rule is required or recommended rule  
 *
 * @param {String} Returns "Yes" if required, otherwise "No" 
 */

OpenAjax.a11y.NodeResult.prototype.getRuleRequiredYesNo = function () {

  return this.rule_result.getRuleRequiredYesNo();
  
};

/**
 * @method getRuleRequiredOrRecommended
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Returns 'Required' or "Recommended' depending on whether the rule is required or recommended rule  
 *
 * @param {String} Returns "Required" if required, otherwise "Recommended" 
 */

OpenAjax.a11y.NodeResult.prototype.getRuleRequiredOrRecommended = function () {

  return this.rule_result.getRuleRequiredOrRecommended();
  
};


/**
 * @method getRuleId
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Get the programmatic id that uniquely identifies the rule
 *
 * @return {String} The rule id
 */

OpenAjax.a11y.NodeResult.prototype.getRuleId = function () {

  return this.rule_result.getRuleId();
   
};

/**
 * @method getRuleIdNLS
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Get a localized human readable id for uniquely identifying the rule
 *
 * @return {String} Localized string of the rule id
 */

OpenAjax.a11y.NodeResult.prototype.getRuleIdNLS = function () {

  return this.rule_result.getRuleIdNLS();
   
};


/**
 * @method getRuleCategory
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Get a localized title, url and description of the rule category
 * 
 * @return {Object}  Returns an object with the following propertues:<br/>
 *                   'title':  String representing the Title of the rule category <br/>
 *                   'desc':   String providing a longer description of the rule category<br/>
 *                   'url':    URL to more information about the rule category (maybe blank)<br/>
 */

OpenAjax.a11y.NodeResult.prototype.getRuleCategory = function () {

  return this.rule_result.getRuleCategory();
  
};

/**
 * @method getRuleCategoryConstant
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Returns the numeric value for the rule category
 * 
 * @return {Number}  Numeric value of the rule category
 */

OpenAjax.a11y.NodeResult.prototype.getRuleCategoryConstant = function () {

  return this.rule_result.getRuleCategoryConstant();
  
};


/**
 * @method getRuleScope
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Get the rule scope constant of the rule 
 *
 * @return {Number} rule scope constant
 */
 
OpenAjax.a11y.NodeResult.prototype.getRuleScope = function () {

  return this.rule_result.getRuleScope(); 
  
};

/**
 * @method getRuleScopeNLS
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Get a localized string of the rule scope (i.e. 'element' or 'page')
 *
 * @return {String} Localized string of the rule scope
 */
 
OpenAjax.a11y.NodeResult.prototype.getRuleScopeNLS = function () {

  return this.rule_result.getRuleScopeNLS(); 
  
};



/**
 * @method isScopeWebsite
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Test if the rule is a website level rule
 *
 * @return {Boolean} True if the rule has a scope of website, otherwise false
 */
 
OpenAjax.a11y.NodeResult.prototype.isScopeWebsite = function () {

  return this.rule_result.isScopeWebsite();
  
};

/**
 * @method isScopePage
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Returns an localized string of the rule scope (i.e. element or page)
 *
 * @return {Boolean} True if the rule has a scope of page, otherwise false
 */
 
OpenAjax.a11y.NodeResult.prototype.isScopePage = function () {

  return this.rule_result.isScopePage(); 
  
};

/**
 * @method isScopeElement
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Returns an localized string of the rule scope (i.e. element or page)
 *
 * @return {Boolean} True if the rule has a scope of element, otherwise false
 */
 
OpenAjax.a11y.NodeResult.prototype.isScopeElement = function () {

  return this.rule_result.isScopeElement(); 
  
};

/**
 * @method getRuleDefinition
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Gets the definition of the rule
 *
 * @return {String} Localized string of the rule definition based on being 
 *                  required or recommended
 */

OpenAjax.a11y.NodeResult.prototype.getRuleDefinition = function () {

  return this.rule_result.getRuleDefinition();
  
};

/**
 * @method getRuleSummary
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc  Gets the summary of the rule
 *
 * @return {String} Localized string of the rule summary based on being 
 *                  required or recommended
 */

OpenAjax.a11y.NodeResult.prototype.getRuleSummary = function () {

  return this.rule_result.getRuleSummary();
  
};


/**
 * @method getPurpose
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Gets an array strings representing the purpose, basically
 *       how does the rule help people with disabilities
 *
 * @return  {Array}  Returns an array of localized string describing the purpose
 */
OpenAjax.a11y.NodeResult.prototype.getPurpose = function () {

  return this.rule_result.getPurpose();

};


/**
 * @method getTargetResourcesDescription
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Get a description of the markup or page feature the rule is evaluates
 *
 * @return  {String}  Localized string representing the markup or page feature 
 *                    tested by the rule
 */
OpenAjax.a11y.NodeResult.prototype.getTargetResourcesDescription = function () {

  return this.rule_result.getTargetResourcesDescription();

};

/**
 * @method getTargetResources
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Returns an localized array strings representing target resources of 
 *       the rule
 *
 * @return  {Array}  Returns an array of strings identifying the elements and/or
 *                    attributes that the rule evaluates
 */
OpenAjax.a11y.NodeResult.prototype.getTargetResources = function () {

  return this.rule_result.getTargetResources();

};


/**
 * @method getTechniques
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Get the techniques to implement the requirements of the rule 
 *
 * @return  {Array}  Returns an array of objects with localized strings and urls.<br/>
 *                   Each object has the following properties:<br/>
 *                   'title'    : Localized string describing the technique<br/> 
 *                   'url': URL to more information about the technique<br/>
 */
 
OpenAjax.a11y.NodeResult.prototype.getTechniques = function () {

  return this.rule_result.getTechniques();
  
};

/**
 * @method getManualCheckProcedures
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Gets manual checking proceedures for evaluating the rule
 *       requirements
 *
 * @return  {Array}  Returns an array of objects with localized strings and urls.<br/>
 *                   Each object has the following properties:<br/>
 *                   'title' : Localized string describing the technique<br/> 
 *                   'url': URL to more information about the technique<br/>
 */
 
OpenAjax.a11y.NodeResult.prototype.getManualCheckProcedures = function () {

  return this.rule_result.getManualCheckProcedures();

};

/**
 * @method getInformationalLinks
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Get information links related to understanding or implementation of the rule
 *
 * @return  {Array}  Returns an array of objects, each object includes the following properties:<br/>
 *                   'type_const' : Number representing the type of information,<br/> 
 *                   'title'      : Title descriping the type of information, <br/>
 *                   'url'        : Link to more information <br/>
 *
 * @example
 *
 * var node_list = [];
 * var info_links = rule.getInformationalLinks();
 * 
 * for(var i = 0; i < info_links.length; i++) {
 *   var info_link = info_links[i];
 *
 *   // Using object properties to create a link element
 *   var node = document.createElement('a');
 *   node.appendChild(document.createTextNode(info_link.title));
 *   node.setAttribute('href',  info_link.url);
 *   node.setAttribute('class', info_link.type_const.toString());
 *
 *   node_list.push(node);
 * }
 */
OpenAjax.a11y.NodeResult.prototype.getInformationalLinks = function () {

  return this.rule_result.getInformationalLinks();

};


/**
 * @method getPrimarySuccessCriterion
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Get information about primary WCAG 2.0 Success Criteria for the rule
 *
 * @return  {Object}  Object representing the success criteria, 
 *                    each object has the following properties:<br/> 
 *                    'id'           : A "P.G.SC" formatted string representing the SC,<br/>  
 *                    'title'        : A localized title for the SC,<br/>
 *                    'description'  : A localized description of the SC,<br/>
 *                    'url'          : A url to the SC in the WCAG 2.0 document<br/>
 *                    'level'        : The level of the Success Criterion (e.g. A, AA or AAA)<br/>
 *
 * @example
 *
 * var sc_info = rule.getPrimarySuccessCriterion();
 * 
 * // Creating a link element to the primary success criterion
 * var node = document.createElement('a');
 * node.appendChild(document.createTextNode(sc_info.id + " " + sc_info.title));
 * node.setAttribute('href',  sc_info.url);
 * node.setAttribute('title', sc_info.description);
 * } 
 */

OpenAjax.a11y.NodeResult.prototype.getPrimarySuccessCriterion = function () {

  return this.rule_result.getPrimarySuccessCriterion();

};

/**
 * @method getRelatedSuccessCriteria
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Get information about the related WCAG 2.0 Success Criteria for the rule
 *
 * @return  {Array}  Array of objects representing the success criteria, each object has the 
 *                   following properties: <br/>
 *                   'id'           : A "P.G.SC" formatted string representing the SC, <br/> 
 *                   'title'        : A localized title for the SC,<br/>
 *                   'description'  : A localized description of the SC,<br/>
 *                   'url'          : A url to the SC in the WCAG 2.0 document<br/>
 *                   'level'        : The level of the Success Criterion (e.g. A, AA or AAA)<br/>
 */

OpenAjax.a11y.NodeResult.prototype.getRelatedSuccessCriteria = function () {

  return this.rule_result.getRelatedSuccessCriteria();

};

/**
 * @method getWCAG20LevelConstant
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Get the numerical constant for the WCAG 2.0 Success Criterion Level 
 *       based on the primary id of the rule
 *
 * @return  {Number}  Number representing the WCAG 2.0 level 
 */

OpenAjax.a11y.NodeResult.prototype.getWCAG20LevelConstant = function () {

  return this.rule_result.getWCAG20LevelConstant();

};

/**
 * @method getWCAG20Level
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Get the string representation of the the WCAG 2.0 Success Criterion Level 
 *       based on the primary id of the rule
 *
 * @return  {String}  String representing the WCAG 2.0 success criterion level 
 *                    (i.e. A, AA or AAA)
 */

OpenAjax.a11y.NodeResult.prototype.getWCAG20Level = function () {

  return this.rule_result.getWCAG20Level();

};

/**
 * @method getXPath
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Returns the xpath of the associated element
 * 
 * @return {String} information about the node result 
 */

OpenAjax.a11y.NodeResult.prototype.getXPath = function () {
  
  return this.getDOMElement().xpath;
   
};


/**
 * @method getDOMElement
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Returns the dom element object
 *
 * @return {String} Returns a dom element associated with the cache item
 */

OpenAjax.a11y.NodeResult.prototype.getDOMElement = function () {

  // Get DOMElement object: If cache_item does not have dom_element property
  // referencing an object, then cache_item is itself a DOMElement.
  return (typeof this.cache_item.dom_element === 'object') ?
          this.cache_item.dom_element : this.cache_item;
                     
                     
};

/**
 * @method getCacheItem
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Gets the cache item associated with the node result
 *
 * @return {Object} Returns a cache item object
 */

OpenAjax.a11y.NodeResult.prototype.getCacheItem = function () {

  return this.cache_item;
 
};


/**
 * @method getPropertyValue
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Returns the value of a property in the cache 
 *
 * @param  {String}  property  -  Property of the cache element object 
 *
 * @return {value | null} Returns a value if property is defined, null if not
 */

OpenAjax.a11y.NodeResult.prototype.getPropertyValue = function (property) {

  var value;

  value = this.cache_item[property];  
  if (typeof value == 'string' || typeof value == 'boolean' || typeof value == 'number') return value;
  
  value = this.cache_item.dom_element[property]; 
  if (value || typeof value == 'boolean' || typeof value == 'number') return value;  

  value = this.cache_item.dom_element.computed_style[property]; 
  if (value || typeof value == 'boolean' || typeof value == 'number') return value;  

  value = this.cache_item.dom_element.events[property]; 
  if (value || typeof value == 'boolean' || typeof value == 'number') return value;  
   
   
  return null;
  
};

/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Creates a text string representation of the node result object 
 *
 * @return {String} Returns a text string representation of the node result object
 */

OpenAjax.a11y.NodeResult.prototype.toString = function () {

  return this.getResultMessage();
  
};


/**
 * @method toJSON
 *
 * @memberOf OpenAjax.a11y.NodeResult
 *
 * @desc Creates JSON object descibing the properties of the node result
 *
 * @param {String} prefix  -  A prefix string typically spaces
 * 
 * @return String information about the node result 
 */

OpenAjax.a11y.NodeResult.prototype.toJSON = function (prefix) {

  var next_prefix = "";
  
  if (typeof prefix !== 'string' || prefix.length === 0) prefix = "";
  else next_prefix = prefix + "    ";

  var result_value_nls = this.getResultValueStringsNLS();
  
  var json = "";

  json += prefix + "{ \"result_label\"    : \"" + result_value_nls.label      + "\",";
  json += prefix + "  \"result_style\"    : \"" + result_value_nls.style      + "\",";
  json += prefix + "  \"result_abbrev\"   : \"" + result_value_nls.abbrev     + "\",";
  json += prefix + "  \"rule_id\"         : \"" + this.getRuleId()            + "\",";
  json += prefix + "  \"nls_rule_id\"     : \"" + this.getRuleIdNLS()         + "\",";
  json += prefix + "  \"required\"        : "   + this.isRuleRequired()       + ",";
  json += prefix + "  \"required_nls\"    : \"" + this.getRuleRequiredYesNo() + "\",";
  json += prefix + "  \"wcag_primary_id\" : \"" + this.getPrimarySuccessCriterion().id  + "\",";
  json += prefix + "  \"wcag_level\"      : \"" + this.getWCAG20Level()     + "\",";
  json += prefix + "  \"message\"         : \"" + OpenAjax.a11y.util.escapeForJSON(this.getResultMessage()) + "\",";

  var result_props     = this.getTargetResourceProperties();
  var result_props_len = result_props.length;
  var last = result_props_len - 1;
  
  if (result_props_len > 0) {
    json += prefix + "  \"properties\" : [";
    for (var i = 0; i < result_props_len; i++) {
      var result_prop = result_props[i];
      if (i === last) json += next_prefix + "{ \"label\" : \"" + result_prop.label + "\", \"value\" : \"" + OpenAjax.a11y.util.escapeForJSON(result_prop.value) + "\"}";
      else json += next_prefix + "{ \"label\" : \"" + result_prop.label + "\", \"value\" : \"" + OpenAjax.a11y.util.escapeForJSON(result_prop.value) + "\"},";
    }
    json += prefix + "  ]";
  }
  else {
    json += prefix + "  \"properties\" : []";
  }
    
  json += prefix + "}";
  
  return json;
};

/*
 * Copyright 2011-2013 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* ---------------------------------------------------------------- */
/*                      FilteredCacheItemResults                    */
/* ---------------------------------------------------------------- */

/**
 * @constructor FilteredCacheItemResults
 *
 * @memberOf OpenAjax.a11y
 *
 * @desc Constructs a data structure of cache items associated with a rule category 
 *       The cache items returned can be filtered by the tpe of evaluation result 
 *
 * @param  {Object}  evaluation_result  - Evaluation result object containing the evaluation results
 *
 * @param  {String}  title     - Title for the group 
 * @param  {String}  url       - URL to more information on the group  
 * @param  {String}  desc      - Description of the group 
 */ 

/**
 * @private
 * @constructor Internal Properties
 *
 * @property  {Boolean} is_tree       - At least one of the CacheItemResults contains child items
 * @propert   {Object}  ruleset       - Ruleset object containing the evaluation results
 * @property  {Object}  dom_cache     - dom cache to use in generating filtered results 
 *
 * @property  {Number}  element_type  - Element type(s) included in this cache list;
 * @property  {Number}  filter        - Node result filter used on the list
 * 
 * @property  {Array}   cache_item_results        - list of top level cache item results
 * 
 * @property  {Boolean}  has_elements  - True if group contains at least one element 
 *
 * @property  {Boolean}  summary_result     - Summary of the node results for 
 *                                            the rule  
 */

 OpenAjax.a11y.FilteredCacheItemResults = function(eval_results, title, url, desc) {

  this.title = "";
  this.url = "";
  this.desc = "";
  
  if (typeof title === 'string') this.title = title;
  if (typeof url   === 'string') this.url = url;
  if (typeof desc  === 'string') this.desc = desc;

  this.is_tree = false;
  this.evaluation_results = eval_results;
  this.dom_cache = eval_results.dom_cache;
  
  this.element_type = OpenAjax.a11y.ELEMENT_TYPE.UNDEFINED;
  this.filter       = OpenAjax.a11y.RESULT_FILTER.ALL;
  
  this.has_elements = false;
  
  this.node_result_summary = new OpenAjax.a11y.NodeResultSummary();
  
  this.rule_results_summary = new OpenAjax.a11y.RuleResultsSummary();
      
  this.cache_item_results = [];
  
};

/**
 * @method getTitle
 *
 * @memberOf OpenAjax.a11y.FilteredCacheItemResults
 *
 * @desc Returns the title of the group
 *
 * @return  {String} String representing the title of the group
 */

OpenAjax.a11y.FilteredCacheItemResults.prototype.getTitle = function() {

  return this.title;
};

/**
 * @method getURL
 *
 * @memberOf OpenAjax.a11y.FilteredCacheItemResults
 *
 * @desc Returns the url of the group, can be empty
 *
 * @return  {String} String of the url to more information about a group
 */

OpenAjax.a11y.FilteredCacheItemResults.prototype.getURL = function() {

  return this.url;
};

/**
 * @method getDescription
 *
 * @memberOf OpenAjax.a11y.FilteredCacheItemResults
 *
 * @desc Returns the description of the group, can be empty
 *
 * @return  {String} String describing the group of rules
 */

OpenAjax.a11y.FilteredCacheItemResults.prototype.getDescription = function() {

  return this.desc;
};

/**
 * @method isTree
 *
 * @memberOf OpenAjax.a11y.FilteredCacheItemResults
 *
 * @desc Tests if the cache items has a tree structure (otherwise simple list)
 *
 * @return  {Boolean}  true if cache items are organized as a tree, otherwise false
 */

OpenAjax.a11y.FilteredCacheItemResults.prototype.isTree = function() {

  return this.is_tree;
};


 /**
 * @method getNodeResultSummary
 *
 * @memberOf OpenAjax.a11y.FilteredCacheItemResults
 *
 * @desc Gets numerical summary information about the node results 
 *
 * @return {NodeResultSummary} Returns the NodeResultSummary object 
 */
 
OpenAjax.a11y.FilteredCacheItemResults.prototype.getNodeResultSummary = function () {

  return this.node_result_summary;

};

 /**
 * @method getRuleResultsSummary
 *
 * @memberOf OpenAjax.a11y.FilteredCacheItemResults
 *
 * @desc Gets numerical summary information about the rule results 
 *
 * @return {RuleResultsSummary} Returns the rule result summary object 
 */
OpenAjax.a11y.FilteredCacheItemResults.prototype.getRuleResultsSummary = function () {

  return this.rule_results_summary;

};




/**
 * @method hasResults
 *
 * @memberOf OpenAjax.a11y.FilteredCacheItemResults
 *
 * @desc Tests if any of the rules in this group applied to the content in the page 
 *
 * @return {Boolean} True if any of the rule have results, otherwise false
 */
 
OpenAjax.a11y.FilteredCacheItemResults.prototype.hasResults = function () {

   return this.node_result_summary.hasResults();

};

/**
 * @method hasElements
 *
 * @memberOf OpenAjax.a11y.FilteredCacheItemResults
 *
 * @desc Tests if any of their are any elements of this type  
 *
 * @return {Boolean} True if the group contains at least one element, otherwise false
 */
 
OpenAjax.a11y.FilteredCacheItemResults.prototype.hasElements = function () {

   return this.has_elements;

};

/**
 * @method updateSummary
 *
 * @memberOf OpenAjax.a11y.FilteredCacheItemResults
 * 
 * @desc Updated summary result counts with cache item results
 *
 * @param  {Array}   cache_item_result  - Cache item result to add to summary information
 */
 
OpenAjax.a11y.FilteredCacheItemResults.prototype.updateSummary = function(cache_item_result) {

  this.node_result_summary.addNodeResultSummary(cache_item_result.getNodeResultSummary());

};

/**
 * @method getCacheItemResults
 *
 * @memberOf OpenAjax.a11y.FilteredCacheItemResults
 *
 * @desc Constructs a data structure of cache items associated with a rule category 
 *       The cache items returned can be filtered by the tpe of evaluation result 
 *          
 * @param  {Number}  element_type             - Number representing the element types to be included
 * @param  {Number}  filter        (optional) - Number representing the evaluation results filter (default all results)
 */

 OpenAjax.a11y.FilteredCacheItemResults.prototype.getCacheItemResults = function(element_type, filter) {

//  OpenAjax.a11y.logger.debug("FILTER: " + filter );

  var total;

  var ELEMENT_TYPE = OpenAjax.a11y.ELEMENT_TYPE;

  this.element_type  = element_type;
  
  if (typeof filter !== 'number') filter = OpenAjax.a11y.RESULT_FILTER.ALL;

  this.filter = filter;

  this.cache_item_results = [];
  
  var ci_result = null;
  
  switch (element_type) {
  
  case ELEMENT_TYPE.ALL:
    this.title = "All Elements";
    this.filterCacheItemsByNodeResultsFromTree(this.dom_cache.element_cache.child_dom_elements, filter, true);
    break;

  case ELEMENT_TYPE.AUDIO_VIDEO:
    this.title = "Audio and Video Elements";
    this.filterCacheItemsByNodeResultsFromTree(this.dom_cache.media_cache.media_elements, filter);
    break;

  case ELEMENT_TYPE.FORM_CONTROLS:
    this.title = "Form Control Elements";
    this.filterCacheItemsByNodeResultsFromTree(this.dom_cache.controls_cache.child_cache_elements, filter);    
    break;

  case ELEMENT_TYPE.HEADINGS_LANDMARKS:
    this.title = "Heading and Landmark Elements";
    var cache =  this.dom_cache.headings_landmarks_cache;

    if (cache.title_element) { 
      ci_result = new OpenAjax.a11y.CacheItemResult(cache.title_element, filter, 1); 
      if (ci_result) this.cache_item_results.push(ci_result);
    }

    if (cache.page_element)  { 
      ci_result = new OpenAjax.a11y.CacheItemResult(cache.page_element, filter, 2); 
      if (ci_result) this.cache_item_results.push(ci_result);
    }
    
    this.filterCacheItemsByNodeResultsFromTree(cache.child_cache_elements, filter, 2);
    
    break;

  case ELEMENT_TYPE.IMAGES:
    this.title = "Image Elements";
    this.filterCacheItemsByNodeResultsFromList(this.dom_cache.images_cache.image_elements, filter);    
    break;

  case ELEMENT_TYPE.LINKS:
    this.title = "Link Elements";
    this.filterCacheItemsByNodeResultsFromList(this.dom_cache.links_cache.link_elements, filter);    
    break;

  case ELEMENT_TYPE.LAYOUT_TABLES:

    if (this.dom_cache.tables_cache.page_element)  { 
      ci_result = new OpenAjax.a11y.CacheItemResult(this.dom_cache.tables_cache.page_element, filter, 1); 
      if (ci_result) this.cache_item_results.push(ci_result);
    }
    
    this.title = "Table Elements";
    this.filterCacheItemsByNodeResultsFromTree(this.dom_cache.tables_cache.child_cache_elements, filter, 1);
    break;

  case ELEMENT_TYPE.TEXT:
    this.title = "Elements with Text Content";
    this.filterCacheItemsByNodeResultsFromList(this.dom_cache.text_cache.text_nodes, filter);
    break;

  case ELEMENT_TYPE.WIDGETS:
    this.title = "Widget Elements";
    this.filterCacheItemsByNodeResultsFromTree(this.dom_cache.controls_cache.child_cache_elements, filter);
    break;

  default:
    break;  
   
  }
  
};

/**
 * @method filterCacheItemsByNodeResultsFromList
 *
 * @memberOf OpenAjax.a11y.FilteredCacheItemResults
 * 
 * @desc Returns a lists of cache item results by filtered node results based on 
 *       the filter.  Does not traverse the children of the cache items
 *
 * @param  {Array}   cache_items  - List of cache element items
 * @param  {Number}  filter       - Number representing the types of results 
 *                                  to include in the array
 * @param  {Number}  start        - Start for the ordinal position
 */
 
OpenAjax.a11y.FilteredCacheItemResults.prototype.filterCacheItemsByNodeResultsFromList = function(cache_items, filter, start) {

  if (typeof start !== 'number') start = 0;

  var index = start + 1;

  this.is_tree = false;

  var RESULT_FILTER = OpenAjax.a11y.RESULT_FILTER;

  var cache_items_len = cache_items.length;
  
  for (var i = 0; i < cache_items_len; i++) {
  
    var ci = cache_items[i];
    
    var ci_result = new OpenAjax.a11y.CacheItemResult(ci, filter, index); 

    index++;

    this.updateSummary(ci_result);

    if (ci_result) this.cache_item_results.push(ci_result);

  } 
  
};  

 
/**
 * @method filterCacheItemsByNodeResultsFromTree
 *
 * @memberOf OpenAjax.a11y.FilteredCacheItemResults
 * 
 * @desc Returns an nested lists of cache item results by node results based on the filter 
 *
 * @param  {Array}    cache_items  - Array of cache element items
 * @param  {Number}   filter       - Number representing the types of results 
 *                                   to include in the array
 * @param  {Boolean}  as_list      - Optional parameter to force result to be a list
 * @param  {Number}   start        - Start for the ordinal position
 *
 * @return {Number}  Number of cache items that were not included due to filter settings
 */
 
OpenAjax.a11y.FilteredCacheItemResults.prototype.filterCacheItemsByNodeResultsFromTree = function(cache_items, filter, as_list, start) {

   function traverseCacheItems(cache_item_result, cache_item) {
  
    var ci_result = new OpenAjax.a11y.CacheItemResult(cache_item, filter, index);
    
    index++;
    
    filtered_cache_item_results.updateSummary(ci_result);

    if (cache_item_result && !as_list) {
      cache_item_result.addChildCacheItemResult(ci_result);
      is_tree = true;
    } else {
      cache_item_results.push(ci_result);
    } 

    var child_cache_elements     = [];
    var child_cache_elements_len = 0;
    
    if (cache_item.child_cache_elements) child_cache_elements = cache_item.child_cache_elements;
    else if (cache_item.child_dom_elements) child_cache_elements = cache_item.child_dom_elements;

    child_cache_elements_len = child_cache_elements.length;

//    OpenAjax.a11y.logger.debug("CI Result: " + ci_result + "   flag: " + OpenAjax.a11y.FilteredCacheItemResults.add_flag + "   children: " + child_cache_elements_len);

    for (var i = 0; i < child_cache_elements_len; i++) {
    
      var cces = child_cache_elements[i];
      traverseCacheItems(ci_result, cces);
      
    }
    
  }

  var RESULT_FILTER = OpenAjax.a11y.RESULT_FILTER;

  var is_tree = false;
  
  var filtered_cache_item_results = this;
  
  if (typeof as_list !== 'boolean') as_list = false;

  if (typeof start !== 'number') start = 0;

  var index = start + 1;

  var cache_item_results = [];

  var cache_items_len = cache_items.length;
  
  var all_flag = (this.filter === RESULT_FILTER.ALL);
  
  for (var i = 0; i < cache_items_len; i++) {
    var ci = cache_items[i];
    traverseCacheItems(null, ci);
  } 
  
  this.is_tree = is_tree;
  this.cache_item_results = cache_item_results;

  OpenAjax.a11y.logger.debug("IS TREE: " + this.is_tree);
  
};  


/**
 * @method toJSON
 *
 * @memberOf OpenAjax.a11y.FilteredCacheItemResults
 *
 * @desc Returns an JSON representation of the filtered cache item results 
 *
 * @param {String} prefix  -  A prefix string typically spaces
 *
 * @return  {String}  JSON string representing the report data 
 */

OpenAjax.a11y.FilteredCacheItemResults.prototype.toJSON = function(prefix, element_type_title) {

  var next_prefix = "";

  if (typeof prefix !== 'string' || prefix.length === 0) prefix = "";
  else next_prefix = prefix + "    ";  

  if (typeof  element_type_title !== 'string' ||  element_type_title.length === 0)  element_type_title = "no element title";

  var json = "";

  var ruleset_title   = this.evaluation_results.getRulesetTitle();
  var ruleset_version = this.evaluation_results.getRulesetVersion();
  var ruleset_id      = this.evaluation_results.getRulesetId();
  
  var eval_title = this.evaluation_results.getTitle();
  var eval_url   = this.evaluation_results.getURL();
  var date       = this.evaluation_results.getDate().split(':');
  var eval_time  = date[1] + ":" + date[2];
  var eval_date  = date[0];

  if (typeof eval_title != 'string' && eval_title.length === 0) eval_title = "no evaluation title";

  json += "{";

  json += prefix + "  \"element_type_title\"   : \"" + OpenAjax.a11y.util.escapeForJSON(element_type_title) + "\",";

  json += prefix + "  \"ruleset_title\"   : \"" + OpenAjax.a11y.util.escapeForJSON(ruleset_title) + "\",";
  json += prefix + "  \"ruleset_version\" : \"" + OpenAjax.a11y.util.escapeForJSON(ruleset_version) + "\",";
  json += prefix + "  \"ruleset_id\"      : \"" + ruleset_id + "\",";

  json += prefix + "  \"eval_title\"    : \"" + OpenAjax.a11y.util.escapeForJSON(eval_title) + "\",";
  json += prefix + "  \"eval_url\"      : \"" + OpenAjax.a11y.util.escapeForJSON(eval_url) + "\",";
  json += prefix + "  \"eval_date\"     : \"" + OpenAjax.a11y.util.escapeForJSON(eval_date) + "\",";
  json += prefix + "  \"eval_time\"     : \"" + OpenAjax.a11y.util.escapeForJSON(eval_time) + "\",";


  if (this.is_tree) json += prefix + "  \"is_tree\" : true,";
  else json += prefix + "  \"is_tree\" : false,";
  
  json += prefix + "  \"results\" : [";
  
  var ci_results = this.cache_item_results;
  var ci_results_len = ci_results.length;
  var ci_results_last = ci_results_len - 1;
  
//  OpenAjax.a11y.logger.debug("  Number of cache results: " + ci_results.length);

  for (var i = 0; i < ci_results_len; i++) {

//    OpenAjax.a11y.logger.debug("  " + i + ": " + ci_results[i].cache_item.cache_id);

    json += ci_results[i].toJSON(next_prefix);
    
    if (i !== ci_results_last) json += ",";
  }  
  json += prefix + "  ]";

  json += prefix + "}";
  
  return json; 

};

/**
 * @method toHTML
 *
 * @memberOf OpenAjax.a11y.FilteredCacheItemResults
 *
 * @desc Returns an HTML representation of the filtered cache item results 
 *
 * @param  {String}  title  -   Title of the report
 *
 * @return  {String}  String representing the HTML for the report 
 */

OpenAjax.a11y.FilteredCacheItemResults.prototype.toHTML = function(title) {

  var evaluation_results = this.evaluation_results;
  
  var wcag20_nls  = OpenAjax.a11y.all_wcag20_nls.getNLS();  

  var html = "";
  
  html += "<!DOCTYPE html>\n";
  html += "<html xml:lang='en' lang='en'>\n";
  html += "  <head>\n";
  html += "    <title>" + title + "</title>\n";
  html += "    <meta charset='ISO-8859-1' />\n";
  html += OpenAjax.a11y.report_css;
  html += "    <script type='text/javascript'>\n";
  html += "      var OAA_REPORT = {};\n";  
  html += "      OAA_REPORT.element_type_data = " + this.toJSON("\n      ", title) + ";\n\n";
  html += "      OAA_REPORT.ruleset = " + evaluation_results.toJSON("\n      ", evaluation_results.getRulesetTitle()) + ";\n\n";
  html += "      OAA_REPORT.wcag20  = " + wcag20_nls.toJSON("\n      ") + ";\n\n";
  html += "    </script>\n";
  html += OpenAjax.a11y.report_element_type_view_js;
  html += "  </head>\n";
  html += OpenAjax.a11y.report_element_type_view_body;
  html += "</html>\n";
  
  return html;
};

/**
 * @method toCSV
 *
 * @memberOf OpenAjax.a11y.FilteredCacheItemResults
 *
 * @desc Returns an CSV representation of the filtered cache item results 
 *
 * @param  {String}  title  -   Title of the report
 *
 * @return  {String}  String representing the CSV for the report 
 */

OpenAjax.a11y.FilteredCacheItemResults.prototype.toCSV = function(title) {

  var eval_title = this.evaluation_result.title;
  var eval_url   = this.evaluation_result.url;
  var date       = this.evaluation_result.date.split(':');
  var eval_time  = date[1] + ":" + date[2];
  var eval_date  = date[0];
  
  if (eval_title.length > 30) eval_title = eval_title.slice(0,27) + "...";
    
  var csv = title + "\n\n";

  csv += "\"OAA ID\"";  
  csv += ",\"Element Description\""; 
  csv += ",\"Element id attribute\""; 
  csv += ",\"Element class attribute\""; 
  csv += ",\"Parent Landmark\""; 
  csv += ",\"Rule ID\"";
  csv += ",\"Type\"";
  csv += ",\"WCAG 2.0 Success Criterion\"";
  csv += ",\"WCAG 2.0 Level\"";
  csv += ",\"Severity\"";
  csv += ",\"Evaluation Result Message\"";
  csv += ",\"Evaluation Date\"";
  csv += ",\"Evaluation Time\"";
  csv += "\"URL Evaluated\"";
  csv += ",\"Title of URL Evaluated\"";
  csv += "\n";

  var result_items     = this.cache_item_results;
  var result_items_len = result_items.length;

  for (var i = 0; i < result_items_len; i++) {
         
     var position = i+1;
            
     var result_item      = result_items[i];
     var node_results     = result_item.node_results;
     var node_results_len = node_results.length;
            
     for (var j = 0; j < node_results_len; j++) {
            
       var node_result = node_results[j];
       
       var dom_element = result_item.cache_item;
       
       if (typeof result_item.cache_item.dom_element ===  'object') dom_element = result_item.cache_item.dom_element;
       else dom_element = result_item.cache_item;
       
       var cache_item = node_result.getCacheItem();
               
       csv += "\"" + cache_item.cache_id; 
       csv += "\",\"" + OpenAjax.a11y.util.escapeForJSON(cache_item.toString()); 
       csv += "\",\"" + dom_element.getId(); 
       csv += "\",\"" + dom_element.getClassName(); 
       csv += "\",\"" + dom_element.getParentLandmark(); 
       csv += "\",\"" + node_result.getRuleIdNLS() + ": " + OpenAjax.a11y.util.escapeForJSON(node_result.getRuleSummary());
       csv += "\",\"" + node_result.getRuleType();
       csv += "\",\"" + node_result.getPrimarySuccessCriterion();
       csv += "\",\"" + node_result.getWCAG20Level();
       csv += "\",\"" + node_result.getResultValue().label;
       csv += "\",\"" + OpenAjax.a11y.util.escapeForJSON(node_result.getResultMessage());
       csv += "\",\"" + eval_date;
       csv += "\",\"" + eval_time;
       csv += "\",\"" + OpenAjax.a11y.util.escapeForJSON(eval_title);
       csv += "\",\"" + eval_url;
       csv += "\"\n";
               
    }
  }   

  return csv;
};


/* ---------------------------------------------------------------- */
/*                           CacheItemResult                        */
/* ---------------------------------------------------------------- */

/**
 * @constructor CacheItemResult
 *
 * @memberOf OpenAjax.a11y
 *
 * @desc Represents one of the cache items in a cache items filter result object
 *          
 * @param  {CacheItem}  cache_item  - cache item to be included in filtered results
 * @param  {Number}     filter      - Number representing the types of results 
 *                                    to include in the array
 * @param  {Number}     pos         - Position in a list
 */ 

/**
 * @private
 * @constructor Internal Properties
 *
 * @property {Object}  cache_item  - Cache item object
 *
 * @property {Number}  ordinal_position      - Position in a list
 *
 * @property {Array}   filtered_node_results - Filtered node results of the cache item 
 *
 * @property  {SummaryResult}  summary_result - Summary of the node results for 
 *                                            the cache item  
 *
 * @property {Number}  number_of_node_results_filtered  - Number of node results that 
 *                                            have been filtered from the list
 *
 * @property {Array}   children  - Array of cache item result objects  (if there
 *                                 are parent/child relationships, like headings
 *                                 and landmarks)
 */

 OpenAjax.a11y.CacheItemResult = function(cache_item, filter, pos) {

  function addNodeResultsWebsite(node_results) {  
  
    var node_results_len = node_results.length;
    var count = 0;
    
    for (var i = 0; i < node_results_len; i++) {
      var node_result = node_results[i];
      if (node_result.isScopeWebsite()) {
        filtered_node_results.push(node_result);
        count++;
      }  
    }
    
    return count;
  }

  function addNodeResultsPage(node_results) {  
  
    var node_results_len = node_results.length;
    var count = 0;
    
    for (var i = 0; i < node_results_len; i++) {
      var node_result = node_results[i];
      if (node_result.isScopePage()) {
        filtered_node_results.push(node_result);
        count++;
      }  
    }
    
    return count;
  }

  function addNodeResultsElement(node_results) {  
  
    var node_results_len = node_results.length;
    var count = 0;
    
    for (var i = 0; i < node_results_len; i++) {
      var node_result = node_results[i];
      if (node_result.isScopeElement()) {
        filtered_node_results.push(node_result);
        count++;
      }  
    }
    
    return count;
  }

  function addNodeResults(node_results) {  
  
    var node_results_len = node_results.length;
    
    for (var i = 0; i < node_results_len; i++) filtered_node_results.push(node_results[i]);
    
    return node_results_len;
  }


  this.cache_item = cache_item;
  
  this.filtered_node_results     = [];

  this.number_of_node_results_filtered = 0;

  this.is_tree = false;

  this.ordinal_position = pos;
  
  var rs = new OpenAjax.a11y.NodeResultSummary();
  
  this.children = [];

  var RESULT_FILTER = OpenAjax.a11y.RESULT_FILTER;
  
  var filtered_node_results = this.filtered_node_results;
  
  var de = cache_item;
  if (typeof cache_item.dom_element  != 'undefined') de = cache_item.dom_element;
  
  if (RESULT_FILTER.VIOLATION            & filter) rs.addViolations(addNodeResults(de.rules_violations));
  if (RESULT_FILTER.WEBSITE_MANUAL_CHECK & filter) rs.addManualChecks(addNodeResultsWebsite(de.rules_manual_checks));
  if (RESULT_FILTER.PAGE_MANUAL_CHECK    & filter) rs.addManualChecks(addNodeResultsPage(de.rules_manual_checks));
  if (RESULT_FILTER.ELEMENT_MANUAL_CHECK & filter) rs.addManualChecks(addNodeResultsElement(de.rules_manual_checks));
  if (RESULT_FILTER.WARNING              & filter) rs.addWarnings(addNodeResults(de.rules_warnings));
  if (RESULT_FILTER.PASS                 & filter) rs.addPassed(addNodeResults(de.rules_passed));
  if (RESULT_FILTER.HIDDEN               & filter) rs.addHidden(addNodeResults(de.rules_hidden));     
  
  this.number_of_node_results_filtered  = (de.rules_passed.length        - rs.passed);
  this.number_of_node_results_filtered += (de.rules_violations.length    - rs.violations);
  this.number_of_node_results_filtered += (de.rules_warnings.length      - rs.warnings);
  this.number_of_node_results_filtered += (de.rules_manual_checks.length - rs.manual_checks);
  this.number_of_node_results_filtered += (de.rules_hidden.length        - rs.hidden);

  this.node_result_summary = rs;
      

};

/**
 * @method addChildCacheItemResult
 *
 * @memberOf OpenAjax.a11y.CacheItemResult
 *
 * @desc Adds a cache item result to the children list of a cache item result object 
 *          
 * @param  {CacheItem Object}  cache_item  - cache item to be included in filtered results
 */

OpenAjax.a11y.CacheItemResult.prototype.addChildCacheItemResult = function(cache_item) {

  if (cache_item) { 
    this.children.push(cache_item);
    this.is_tree = true;
  }  

};


/**
 * @method isTree
 *
 * @memberOf OpenAjax.a11y.CacheItemResult
 *
 * @desc Tests if the cache items has a tree structure (otherwise simple list)
 *
 * @return  {Boolean}  true if cache items are organized as a tree, otherwise false
 */

OpenAjax.a11y.CacheItemResult.prototype.isTree = function() {

  return this.is_tree;
};

/**
 * @method agetCacheItem
 *
 * @memberOf OpenAjax.a11y.CacheItemResult
 *
 * @desc Get cache item object 
 *          
 * @return  {Object}  Cache item object
 */

OpenAjax.a11y.CacheItemResult.prototype.getCacheItem = function() {

  return this.cache_item;

};


/**
 * @method getNodeResultSummary
 *
 * @memberOf OpenAjax.a11y.CacheItemResult
 *
 * @desc Gets numerical summary information about the cache item results 
 *
 * @return {ResultSummary} Returns the ResultSummary object 
 *
 */
 
OpenAjax.a11y.CacheItemResult.prototype.getNodeResultSummary = function () {

  return this.node_result_summary;

};

/**
 * @method hasResults
 *
 * @memberOf OpenAjax.a11y.CacheItemResult
 *
 * @desc Tests if node has any rule results
 *          
 * @param  {Boolean}  True if there are rule results, otherwise false
 */

OpenAjax.a11y.CacheItemResult.prototype.hasResults = function() {

  return this.node_result_summary.hasResults();

};

/**
 * @method getHighestResultValueConstant
 *
 * @memberOf OpenAjax.a11y.CacheItemResult
 *
 * @desc Gets highest result value for the node results for this cache item
 *
 * @return {Number} Number representing the highest result value 
 */
 
OpenAjax.a11y.CacheItemResult.prototype.getHighestResultValueConstant = function () {

  var RESULT_VALUE =  OpenAjax.a11y.RESULT_VALUE;
  
  var rs = this.getNodeResultSummary();
  
  if (rs.violations)    return RESULT_VALUE.VIOLATION;
  if (rs.warnings)      return RESULT_VALUE.WARNING;
  if (rs.manual_checks) return RESULT_VALUE.MANUAL_CHECK;
  if (rs.passed)        return RESULT_VALUE.PASS;
  if (rs.hidden)        return RESULT_VALUE.HIDDEN;
  
  return RESULT_VALUE.NONE;

};


/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.CacheItemResult
 *
 * @desc Gets an array of node results for the cache item 
 *          
 * @param  {Array}  Array of node results 
 */

OpenAjax.a11y.CacheItemResult.prototype.getNodeResults = function() {

  return this.filtered_node_results;

};

/**
 * @method getOrdinalPosition
 *
 * @memberOf OpenAjax.a11y.CacheItemResult
 *
 * @desc Gets ordinal position of the cache item in the DOM, 1 based
 *          
 * @param  {Number}  Number representing the ordinal position
 */

OpenAjax.a11y.CacheItemResult.prototype.getOrdinalPosition = function() {

  return this.ordinal_position;

};


/**
 * @method toJSON
 *
 * @memberOf OpenAjax.a11y.CacheItemResult
 *
 * @desc Returns a JSON representation of the cache item 
 *          
 * @param {String} prefix  -  A prefix string typically spaces
 *
 * @return  {String}  String representing the cache item result object
 */

OpenAjax.a11y.CacheItemResult.prototype.toJSON = function(prefix) {

  var next_prefix = "";
  var next_prefix_2 = "";

  if (typeof prefix !== 'string' || prefix.length === 0) prefix = "";
  else {
    next_prefix = prefix + "  ";  
    next_prefix_2 = next_prefix + "  ";  
  }  

  var i;
  var json = "";
  
  var rs = this.getNodeResultSummary();
  
  json += prefix + "{ \"cache_item\"    : \"" + OpenAjax.a11y.util.escapeForJSON(this.cache_item.toString()) + "\",";
  json += prefix + "  \"cache_id\"      : \"" + this.cache_item.cache_id + "\",";
  json += prefix + "  \"violations\"    : "  + rs.violations    + ",";
  json += prefix + "  \"manual_checks\" : "  + rs.manual_checks + ",";
  json += prefix + "  \"warnings\"      : "  + rs.warnings      + ",";
  json += prefix + "  \"passed\"        : "  + rs.passed        + ",";
  json += prefix + "  \"hidden\"        : "  + rs.hidden        + ",";
  json += prefix + "  \"total\"         : "  + rs.total         + ",";
  
  json += prefix + "  \"filtered\"      : "  + this.number_of_node_results_filtered  + ",";

  if (this.filtered_node_results.length > 0) {
    json += prefix + "  \"node_results\" : [";
    
    var n_results      = this.filtered_node_results;
    var n_results_len  = n_results.length;
    var n_results_last = n_results_len - 1;
    
    for (i = 0; i < n_results_len; i++) {
      json += n_results[i].toJSON(next_prefix);
      if (i !== n_results_last) json += ","; 
    }  
    json += prefix + "  ],";
  }
  else {
    json += prefix + "  \"node_results\" : [],";
  }

  if (this.children.length > 0) {
    json += prefix + "  \"children\" : [";
    
    var children      = this.children;
    var children_len  = children.length;
    var children_last = children_len - 1;
    
    for (i = 0; i < children_len; i++) {
      json += children[i].toJSON(next_prefix_2);
      if (i !== children_last) json += ','; 
    }  
    json += prefix + "  ]";
  }
  else {  
    json += prefix + "  \"children\" : []";
  }
  
  json += prefix + "}";

  return json;

};
/*
 * Copyright 2011-2013 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* ---------------------------------------------------------------- */
/*                      FilteredRuleResultsGroups                    */
/* ---------------------------------------------------------------- */

/**
 * @constructor FilteredRuleResultsGroups
 *
 * @memberOf OpenAjax.a11y
 *
 * @desc Constructs a data structure of cache items associated with a rule category 
 *       Node results can be filtered when a rule result is added to the group 
 *
 * @param  {Object}  evaluation_result  - evaluation results used to generate 
 *                                        the filtered results
 * @param  {String}  group_id  - id used to identify this grouping of rules
 *                               
 * @param  {String}  title     - Title for the group 
 * @param  {String}  url       - URL to more information on the group  
 * @param  {String}  desc      - Description of the group 
 *
 * @property  {EvaluationResult} evaluation_result - ruleset and evaluation results 
 *                                                   used to generate the filtered results
 *
 * @property  {String}  group_id  - ID to uniquely identify this group among other 
 *                                  grouping objects
 *
 * @property  {Array}   filtered_rule_results   - array of filtered rule result objects 
 *
 * @property  {Array}   filtered_node_results   - array of node results for the group
 *
 * @property  {String}  title - title for the group of rules
 * @property  {String}  url   - Optional property to provide a link to more information 
 *                              about a group
 * @property  {String}  desc  - Optional property to provide a description of the group
 *
 * @property  {Number}  node_results_filtered_out    - number of node results filtered
 *
 * @property  {Object}  group_information   - Information on the number and types of rules
 *
 * @property  {Boolean}  has_rules          - True if group contains at least one rule 
 *
 * @property  {NodeResultSummary}  node_result_summary  - Summary of the node results for 
 *                                                        the group 
 *
 * @property  {RuleResultsSummary}  rule_results_summary  - Summary of the rule results for 
 *                                                        the group
 */

 OpenAjax.a11y.FilteredRuleResultsGroups = function(eval_result, group_id, title, url, desc) {

  this.evaluation_result = eval_result;
  this.group_id = group_id;

  this.filtered_rule_results_groups = [];

  this.filtered_node_results = [];

  this.title = "";
  this.url = "";
  this.desc = "";
  
  if (typeof title === 'string') this.title = title;
  else this.title = "No title";
  if (typeof url  === 'string') this.url = url;
  if (typeof desc === 'string') this.desc = desc;

  this.has_rules = false;

  this.node_results_filtered_out   = 0;

  this.node_result_summary = new OpenAjax.a11y.NodeResultSummary();  
  
  this.rule_results_summary = new OpenAjax.a11y.RuleResultsSummary();  
  
  this.group_information = new OpenAjax.a11y.info.RuleResultsGroupInfo(title, url, desc);
    
}; 

/**
 * @method getRulesetInfo
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroups
 *
 * @desc Return ruleset of information 
 *
 * @return {RulesetInfo}  RulesetInfo object
 */
 
OpenAjax.a11y.FilteredRuleResultsGroups.prototype.getRulesetInfo = function () {
  return this.evaluation_result.getRulesetInfo();
};

/**
 * @method getPageInfo
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroups
 *
 * @desc Return information on the web page evaluated 
 *
 * @return {PageInfo}  PageInfo object
 */
 
OpenAjax.a11y.FilteredRuleResultsGroups.prototype.getPageInfo = function () {
  return this.evaluation_result.getPageInfo();
};

/**
 * @method getRuleResultsGroupInfo
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroups
 *
 * @desc Return information on the web page evaluated 
 *
 * @return {PageInfo}  PageInfo object
 */
 
OpenAjax.a11y.FilteredRuleResultsGroups.prototype.getRuleResultsGroupInfo = function () {
  return this.group_information;
};



/**
 * @method getTitle
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroups
 *
 * @desc Returns the title of the group
 *
 * @return  {String} String representing the title of the group
 */

OpenAjax.a11y.FilteredRuleResultsGroups.prototype.getTitle = function() {

  return this.title;
};

/**
 * @method getURL
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroups
 *
 * @desc Returns the url of the group, can be empty
 *
 * @return  {String} String of the url to more information about a group
 */

OpenAjax.a11y.FilteredRuleResultsGroups.prototype.getURL = function() {

  return this.url;
};

/**
 * @method getDescription
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroups
 *
 * @desc Returns the description of the group, can be empty
 *
 * @return  {String} String describing the group of rules
 */

OpenAjax.a11y.FilteredRuleResultsGroups.prototype.getDescription = function() {

  return this.desc;
};

/**
 * @method getNumberOfRequiredRules
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroups
 *
 * @desc Get number of required rules in the groups
 *
 * @return  {Number} Number of required rules
 */

OpenAjax.a11y.FilteredRuleResultsGroups.prototype.getNumberOfRequiredRules = function() {

  return this.group_information.required_rules;
};

/**
 * @method getNumberOfRecommendedRules
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroups
 *
 * @desc Get number of recommended rules in the groups
 *
 * @return  {Number} Number of recommended rules
 */

OpenAjax.a11y.FilteredRuleResultsGroups.prototype.getNumberOfRecommendedRules = function() {

  return this.group_information.recommended_rules;
};

/**
 * @method hasResults
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroups
 *
 * @desc Tests if any of the rules in this group applied to the content in the page 
 *
 * @return {Boolean} True if any of the rule have results, otherwise false
 */
 
OpenAjax.a11y.FilteredRuleResultsGroups.prototype.hasResults = function () {

   return this.node_result_summary.hasResults();

};

/**
 * @method hasRules
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroups
 *
 * @desc Tests if any of their are any rules in this group  
 *
 * @return {Boolean} True if the group contains at least one rule, otherwise false
 */
 
OpenAjax.a11y.FilteredRuleResultsGroups.prototype.hasRules = function () {

   return this.has_rules;

};


 /**
 * @method getNodeResultSummary
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroups
 *
 * @desc Gets numerical summary information about the node results 
 *
 * @return {NodeResultSummary} Returns the NodeResultSummary object 
 */
 
OpenAjax.a11y.FilteredRuleResultsGroups.prototype.getNodeResultSummary = function () {

  return this.node_result_summary;

};

/**
 * @method getRuleResultsSummary
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroups
 *
 * @desc Gets numerical summary information about the rule results 
 *
 * @return {RuleResultsSummary} Returns the rule result summary =object  
 */
 
OpenAjax.a11y.FilteredRuleResultsGroups.prototype.getRuleResultsSummary = function () {

  return this.rule_results_summary;

};


/**
 * @method addFilteredRuleResultsGroup
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroups
 *
 * @desc Adds a filtered rule result group to this group for aggregating results
 *
 * @param  {FilteredRuleResultGroup}  filtered_rule_results_group  - Filtered rule result group object to add
 */

OpenAjax.a11y.FilteredRuleResultsGroups.prototype.addFilteredRuleResultsGroup = function(filtered_rule_results_group) {

  if (filtered_rule_results_group) {
    this.filtered_rule_results_groups.push(filtered_rule_results_group);
  }
  
};

/**
 * @method addRuleResult
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroups
 *
 * @desc Adds a rule result to the grouping aggregation of results if the group id has a match in the group
 *
 * @param  {String}      group_id      - id used for matching the rule result object with a filtered rule result group
 * @param  {RuleResult}  rule_result   - Filtered rule result object to aggregate
 * @param  {Number}      filter        - Filter for node results (bit mapped mask)
 *
 * @return  {FilteredRuleResult | object}  Returns a filtered rule result object if rule set was added to this group, 
 *                                         otherwise an return object with an added property   
 */

OpenAjax.a11y.FilteredRuleResultsGroups.prototype.addRuleResult = function(group_id, rule_result, filter) {

  function checkForNewNodes(old_list, new_list) {
  
    for (var i = 0; i < new_list.length; i++) {
    
      var item = new_list[i].cache_item;
    
      if (item.dom_element) item = item.dom_element.cache_id;
      else if (item.parent_element) item = item.parent_element.cache_id;
      else item = 'unknown';

      if (old_list.indexOf(item) === -1) old_list.push(item);
    
    }

    return old_list;
  
  }


  var RETURN_VALUE = OpenAjax.a11y.FILTERED_RULE_RESULT_RETURN_VALUE;

  var groups = this.filtered_rule_results_groups;
  var groups_len = groups.length;

  for (var i = 0; i < groups_len; i++) {
  
     var group = groups[i];

//     OpenAjax.a11y.logger.debug("GROUP: " + group.getTitle());

     var filtered_rule_result = group.addRuleResult(group_id, rule_result, filter);
     
     if (filtered_rule_result.added === RETURN_VALUE.ADDED) {

       this.has_rules = true;

//       OpenAjax.a11y.logger.debug("  Adding rule: " + rule_result.getRuleIdNLS() + " to " + group.getTitle());

       this.filtered_node_results  = checkForNewNodes(this.filtered_node_results, rule_result.node_results_passed);
       this.filtered_node_results  = checkForNewNodes(this.filtered_node_results, rule_result.node_results_violations);
       this.filtered_node_results  = checkForNewNodes(this.filtered_node_results, rule_result.node_results_warnings);
       this.filtered_node_results  = checkForNewNodes(this.filtered_node_results, rule_result.node_results_manual_checks);

       if (rule_result.rule_mapping.required) this.group_information.addToRequiredCount(1);

       this.node_results_filtered_out     += filtered_rule_result.node_results_filtered_out;

       var rs = filtered_rule_result.getNodeResultSummary();

       this.node_result_summary.addNodeResultSummary(rs);        
       
       this.rule_results_summary.addRuleResult(rule_result);
        
       return filtered_rule_result;
     }

  }

  var ro = {};
  ro.added = RETURN_VALUE.NO_MATCH;

  return ro; 

};


/**
 * @method getNumberOfRules
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroups
 *
 * @desc Returns number of rules in this group
 *
 * @return  {Number}  Number of rules in this group 
 */

OpenAjax.a11y.FilteredRuleResultsGroups.prototype.getNumberOfRules = function() {

  return this.group_information.total_rules;

};


/**
 * @method getDocumentObject
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroups
 *
 * @desc Get the document object evaluated
 *
 * @return {Object}  Document object
 */
 
OpenAjax.a11y.FilteredRuleResultsGroups.prototype.getDocumentObject = function () {
  return this.evaluation_result.getDocumentObject();
};

/**
 * @method toJSON
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroups
 *
 * @desc Returns an JSON representation of the rule summary results 
 *
 * @param {String} prefix  -  A prefix string typically spaces
 *
 * @return  {String}  JSON string representing the report data 
 */

OpenAjax.a11y.FilteredRuleResultsGroups.prototype.toJSON = function(prefix) {

  var next_prefix = "";

  if (typeof prefix !== 'string' || prefix.length === 0) prefix = "";
  else next_prefix = prefix + "    ";  

  var group_title = this.title;
  var group_id    = this.group_id;
  
  var ruleset_title   = this.evaluation_result.ruleset_title;
  var ruleset_version = this.evaluation_result.ruleset_version;
  var ruleset_id      = this.evaluation_result.ruleset_id;
  
  var eval_title = this.evaluation_result.title;
  var eval_url   = this.evaluation_result.url;
  var date       = this.evaluation_result.date.split(':');
  var eval_time  = date[1] + ":" + date[2];
  var eval_date  = date[0];

  var json = "";
  
  json += prefix + "{";

  var gi = this.group_information;

  json += prefix + "  \"group_title\"  : \"" + OpenAjax.a11y.util.escapeForJSON(gi.title) + "\",";
  json += prefix + "  \"group_id\"     : \"" + gi.id + "\",";
  
  json += prefix + "  \"ruleset_title\"   : \"" + OpenAjax.a11y.util.escapeForJSON(ruleset_title) + "\",";
  json += prefix + "  \"ruleset_version\" : \"" + OpenAjax.a11y.util.escapeForJSON(ruleset_version) + "\",";
  json += prefix + "  \"ruleset_id\"      : \"" + ruleset_id + "\",";

  json += prefix + "  \"eval_title\"    : \"" + OpenAjax.a11y.util.escapeForJSON(eval_title) + "\",";
  json += prefix + "  \"eval_url\"      : \"" + OpenAjax.a11y.util.escapeForJSON(eval_url)   + "\",";
  json += prefix + "  \"eval_date\"     : \"" + OpenAjax.a11y.util.escapeForJSON(eval_date)  + "\",";
  json += prefix + "  \"eval_time\"     : \"" + OpenAjax.a11y.util.escapeForJSON(eval_time)  + "\",";
  
  json += prefix + "  \"nodes_filtered\"   : \"" + this.node_results_filtered_out   + "\",";
  json += prefix + "  \"required_rules\"   : \"" + gi.required_rules    + "\",";
  json += prefix + "  \"recommened_rules\" : \"" + gi.recommended_rules + "\",";

  json += prefix + "  \"has_rules\"   : \"" + this.has_rules   + "\",";

  var rs = this.node_result_summary;

  json += prefix + "  \"has_results\" : \"" + rs.hasResults()   + "\",";
  json += prefix + "  \"percent\"     : \"" + rs.percent_passed + "\",";
  json += prefix + "  \"passed\"      : \"" + rs.passed         + "\",";
  json += prefix + "  \"violations\"  : \"" + rs.violations     + "\",";
  json += prefix + "  \"warnings\"    : \"" + rs.warnings       + "\",";
  json += prefix + "  \"total\"       : \"" + rs.total          + "\",";
  json += prefix + "  \"manual\"      : \"" + rs.manual_checks  + "\",";
  json += prefix + "  \"hidden\"      : \"" + rs.hidden         + "\",";
  
  json += prefix + "  \"summary_results\" : [";
  
  var results = this.filtered_rule_results_groups;
  var results_len = results.length;
  var results_comma = results_len - 1;
  
  for (var i = 0; i < results_len; i++) {
    json += results[i].toJSON(prefix + "  ", false); 
    if (i < results_comma) json += ","; 
  }  
  
  
  json += prefix + "  ]\n";

  json += prefix + "}";

  return json; 

};


/**
 * @method toHTML
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroups
 *
 * @desc Returns an HTML representation of summary results 
 *
 * @param  {String}  title  -   Title of the report
 *
 * @return  {String}  String representing the HTML for the report 
 */

OpenAjax.a11y.FilteredRuleResultsGroups.prototype.toHTML = function(title) {

  var html = "";
  
  html += "<!DOCTYPE html>\n";
  html += "<html lang='en'>\n";
  html += "  <head>\n";
  html += "    <title>" + title + "</title>\n";
  html += "    <meta charset='ISO-8859-1' />\n";
  html += OpenAjax.a11y.report_css;
  html += "    <script type='text/javascript'>\n";
  html += "      var OAA_REPORT = {};\n";  
  html += "      OAA_REPORT.title = '" + OpenAjax.a11y.util.escapeForJSON(title) + "';\n";  
  html += "      OAA_REPORT.rule_summary_data = " + this.toJSON("\n      ") + ";\n\n";
  html += "    </script>\n";
  html += OpenAjax.a11y.report_rule_summary_view_js;
  html += "  </head>\n";
  html += OpenAjax.a11y.report_rule_summary_view_body;
  html += "</html>\n";
  
  return html;
  
};

/**
 * @method toCSV
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroups
 *
 * @desc Returns an CSV representation of summary results 
 *
 * @param  {String}   title  -   Title of the report
 * @param  {Boolean}  header_flag  -  Optional, set to true if do not want headers for children
 *
 * @return  {String}  String representing the CSV for the report 
 */

OpenAjax.a11y.FilteredRuleResultsGroups.prototype.toCSV = function(title, header_flag) {

  var eval_title = this.evaluation_result.title;
  var eval_url   = this.evaluation_result.url;
  var date       = this.evaluation_result.date.split(':');
  var eval_time  = date[1] + ":" + date[2];
  var eval_date  = date[0];
  
  if (eval_title.length > 30) eval_title = eval_title.slice(0,27) + "...";
    
  var csv = "";
  
  if ((typeof header_flag !== 'boolean') || header_flag) {
    csv += title + "\n\n";
    csv += "\"OAA ID\"";  
    csv += ",\"Element Description\""; 
    csv += ",\"Element id attribute\""; 
    csv += ",\"Element class attribute\""; 
    csv += ",\"Parent Landmark\""; 
    csv += ",\"Rule ID\"";
    csv += ",\"Type\"";
    csv += ",\"WCAG 2.0 Success Criterion\"";
    csv += ",\"WCAG 2.0 Level\"";
    csv += ",\"Severity\"";
    csv += ",\"Evaluation Result Message\"";
    csv += ",\"Evaluation Date\"";
    csv += ",\"Evaluation Time\"";
    csv += "\"URL Evaluated\"";
    csv += ",\"Title of URL Evaluated\"";
    csv += "\n";
  }  

  var results = this.filtered_rule_results_groups;
  var results_len = results.length;
  var results_comma = results_len - 1;
  
  for (var i = 0; i < results_len; i++) {
    csv += results[i].toCSV(title, false); 
  }  

  return csv;  
};



/* ---------------------------------------------------------------- */
/*                      FilteredRuleResultsGroup                    */
/* ---------------------------------------------------------------- */


/**
 * @constructor FilteredRuleResultsGroup
 *
 * @memberOf OpenAjax.a11y
 *
 * @desc Constructs a data structure of cache items associated with a rule category 
 *       Node results can be filtered when a rule result is added to the group 
 *
 * @param  {Object}  evaluation_result  - ruleset and evaluation results used to generate 
 *                              the filtered results
 * @param  {String}  group_id  - id used to identify this grouping of rules and filtering rules
 *
 * @param  {String}  title     - Title for the group 
 * @param  {String}  url       - URL to more information on the group  
 * @param  {String}  desc      - Description of the group 
 *
 * @property  {EvaluationResult} evaluation_result - ruleset and evaluation results 
 *                                                   used to generate the filtered 
 *                                                   results
 *
 * @property  {String}  group_id  - ID to uniquely identify this group among other 
 *                                  grouping objects
 *
 * @property  {Array}   filtered_rule_results   - array of filtered rule result objects 
 *
 * @property  {Array}   filtered_node_results   - array of node results for the group
 *
 * @property  {String}  title  - title for the group of rules
 * @property  {String}  url    - Optional property to provide a link to more information about a group
 * @property  {String}  desc   - Optional property to provide a description of the group
 *
 * @property  {Number}  node_results_filtered_out    - number of node results filtered
 *
 * @property  {Object}  group_information     - Information on rules in the group
 *
 * @property  {Boolean}  has_rules    - True if group contains at least one rule 
 *
 * @property  {NodeResultSummary}  node_result_summary  - Summary of the node results for 
 *                                                        the group 
 *
 * @property  {RuleResultsSummary}  rule_results_summary  - Summary of the rule results for 
 *                                                        the group
 */


OpenAjax.a11y.FilteredRuleResultsGroup = function(eval_result, group_id, title, url, desc) {

  this.evaluation_result = eval_result;
  this.group_id = group_id;

  this.filtered_rule_results = [];

  this.filtered_node_results = [];

  this.title = "";
  this.url = "";
  this.desc = "";
  
  if (typeof title === 'string') this.title = title;
  else this.title = "No title";
  if (typeof url   === 'string') this.url   = url;
  if (typeof desc  === 'string') this.desc  = desc;
  
  this.has_rules = false;
  
  this.node_results_filtered_out = 0;

  this.node_result_summary = new OpenAjax.a11y.NodeResultSummary();  
  
  this.rule_results_summary = new OpenAjax.a11y.RuleResultsSummary();  
  
  this.group_information = new OpenAjax.a11y.info.RuleResultsGroupInfo(title, url, desc);  

};

/**
 * @method getRulesetInfo
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroup
 *
 * @desc Return ruleset of information 
 *
 * @return {RulesetInfo}  RulesetInfo object
 */
 
OpenAjax.a11y.FilteredRuleResultsGroup.prototype.getRulesetInfo = function () {
  return this.evaluation_result.getRulesetInfo();
};

/**
 * @method getPageInfo
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroup
 *
 * @desc Return information on the web page evaluated 
 *
 * @return {PageInfo}  PageInfo object
 */
 
OpenAjax.a11y.FilteredRuleResultsGroup.prototype.getPageInfo = function () {
  return this.evaluation_result.getPageInfo();
};

/**
 * @method getRuleResultsGroupInfo
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroup
 *
 * @desc Return information on the web page evaluated 
 *
 * @return {PageInfo}  PageInfo object
 */
 
OpenAjax.a11y.FilteredRuleResultsGroup.prototype.getRuleResultsGroupInfo = function () {
  return this.group_information;
};




/**
 * @method getTitle
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroup
 *
 * @desc Returns the title of the group
 *
 * @return  {String} String representing the title of the group
 */

OpenAjax.a11y.FilteredRuleResultsGroup.prototype.getTitle = function() {

  return this.title;
};

/**
 * @method getURL
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroup
 *
 * @desc Returns the url of the group, can be empty
 *
 * @return  {String} String of the url to more information about a group
 */

OpenAjax.a11y.FilteredRuleResultsGroup.prototype.getURL = function() {

  return this.url;
};

/**
 * @method getDescription
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroup
 *
 * @desc Returns the description of the group, can be empty
 *
 * @return  {String} String describing the group of rules
 */

OpenAjax.a11y.FilteredRuleResultsGroup.prototype.getDescription = function() {

  return this.desc;
};

/**
 * @method getNumberOfRequiredRules
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroup
 *
 * @desc Get number of required rules in the group
 *
 * @return  {Number} Number of required rules
 */

OpenAjax.a11y.FilteredRuleResultsGroup.prototype.getNumberOfRequiredRules = function() {

  return this.group_information.required_rules;
};

/**
 * @method getNumberOfRecommendedRules
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroup
 *
 * @desc Get number of recommended rules in the group
 *
 * @return  {Number} Number of recommended rules
 */

OpenAjax.a11y.FilteredRuleResultsGroup.prototype.getNumberOfRecommendedRules = function() {

  return this.group_information.recommended_rules;
};


/**
 * @method hasResults
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroup
 *
 * @desc Tests if any of the rules in this group applied to the content in the page 
 *
 * @return {Boolean} True if any of the rule have results, otherwise false
 */
 
OpenAjax.a11y.FilteredRuleResultsGroup.prototype.hasResults = function () {

   return this.node_result_summary.hasResults();

};

/**
 * @method hasRules
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroup
 *
 * @desc Tests if any of their are any rule results in this group  
 *
 * @return {Boolean} True if the group contains at least one rule, otherwise false
 */
 
OpenAjax.a11y.FilteredRuleResultsGroup.prototype.hasRules = function () {

   return this.has_rules;

};


/**
 * @method getNodeResultSummary
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroup
 *
 * @desc Gets numerical summary information about the node results 
 *
 * @return {NodeResultSummary} Returns the node result summary object  
 */
 
OpenAjax.a11y.FilteredRuleResultsGroup.prototype.getNodeResultSummary = function () {

  return this.node_result_summary;

};

/**
 * @method getRuleResultsSummary
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroup
 *
 * @desc Gets numerical summary information about the rule results 
 *
 * @return {RuleResultsSummary} Returns the rule result summary object  
 */
 
OpenAjax.a11y.FilteredRuleResultsGroup.prototype.getRuleResultsSummary = function () {

  return this.rule_results_summary;

};


/**
 * method addRuleResult
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroup
 *
 * @desc Adds a rule result to the grouping aggregation of results if the group id has a match in the group
 *
 * @param  {String}      group_id      - id used for matching the rule result object with a filtered rule result group
 * @param  {RuleResult}  rule_result   - Filtered rule result object to aggregate
 * @param  {Number}      filter        - Filter for node results (bit mapped mask)
 *
 * @return  {FilteredRuleResult | object}  Returns a filtered rule result object if rule set was added to this group, 
 *                                         otherwise an return object with an added property   
 */

OpenAjax.a11y.FilteredRuleResultsGroup.prototype.addRuleResult = function(group_id, rule_result, filter) {

  function checkForNewNodes(old_list, new_list) {
  
    for (var i = 0; i < new_list.length; i++) {
    
      var item = new_list[i].cache_item;
    
      if (item.dom_element) item = item.dom_element.cache_id;
      else if (item.parent_element) item = item.parent_element.cache_id;
      else item = 'unknown';

      if (old_list.indexOf(item) === -1) old_list.push(item);
    
    }

    return old_list;
  
  }

  var ro = {};
  
  var RETURN_VALUE = OpenAjax.a11y.FILTERED_RULE_RESULT_RETURN_VALUE;

//  OpenAjax.a11y.logger.debug("Group ID: " + this.group_id + "  Item ID: " + group_id );  

  if ((this.group_id === group_id) || 
      (typeof this.group_id === 'number' && 
       typeof group_id === 'number' && 
       (this.group_id & group_id))) {

    this.has_rules = true;

//    OpenAjax.a11y.logger.debug("  Adding rule result to group: " + rule_result + "  has rules: " + this.has_rules );  

    this.filtered_node_results = checkForNewNodes(this.filtered_node_results, rule_result.node_results_passed);
    this.filtered_node_results = checkForNewNodes(this.filtered_node_results, rule_result.node_results_violations);
    this.filtered_node_results = checkForNewNodes(this.filtered_node_results, rule_result.node_results_warnings);
    this.filtered_node_results = checkForNewNodes(this.filtered_node_results, rule_result.node_results_manual_checks);
    
    var filtered_rule_result = new OpenAjax.a11y.FilteredRuleResult(rule_result);
      
    filtered_rule_result.getFilteredNodeResults(filter);

    this.filtered_rule_results.push(filtered_rule_result);
    
    if (rule_result.rule_mapping.required) this.group_information.addToRequiredCount(1);
    else this.group_information.addToRecommendedCount(1);

    this.node_results_filtered_out     += filtered_rule_result.node_results_filtered_out;

    var rs = filtered_rule_result.getNodeResultSummary();

//    OpenAjax.a11y.logger.debug(" Group: " + this.group_id + "  Rule " + rule_result.getRuleSummary() + " Total manual checks: " + rs.manual_checks + " New Manual Checks:  " + rs.manual_checks + " manual checks");  

    this.node_result_summary.addNodeResultSummary(rs);

    this.rule_results_summary.addRuleResult(rule_result);

    filtered_rule_result.added = RETURN_VALUE.ADDED;
    
    return filtered_rule_result;
    
  }

  ro.added = RETURN_VALUE.NO_MATCH; 

//  OpenAjax.a11y.logger.debug("  NO MATCH");  

  return ro;

};

/**
 * @method getNumberOfRules
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroup
 *
 * @desc Returns number of rules in this group
 *
 * @return  {Number}  Number of rules in this group 
 */

OpenAjax.a11y.FilteredRuleResultsGroup.prototype.getNumberOfRules = function() {

  return this.group_information.total_rules;

};


/**
 * @method getDocumentObject
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroup
 *
 * @desc Get the document object evaluated
 *
 * @return {Object}  Document object
 */
 
OpenAjax.a11y.FilteredRuleResultsGroup.prototype.getDocumentObject = function () {
  return this.evaluation_result.getDocumentObject();
};


/**
 * @method toJSON
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroup
 *
 * @desc Returns an JSON representation of the rule category results 
 *
 * @param {String}  prefix           -  A prefix string typically spaces
 * @param {Boolean} flag (optional)  -  True (default) to include filtered node results, false to not include 
 *
 * @return  {String}  JSON string representing the report data 
 */

OpenAjax.a11y.FilteredRuleResultsGroup.prototype.toJSON = function(prefix, flag) {

  if (typeof flag !== 'boolean') flag = true;
    
  if (typeof prefix !== 'string' || prefix.length === 0) prefix = "";
  else next_prefix = prefix + "    ";  
  
  var group_title = this.title;
  var group_url   = this.url;
  var group_id    = this.group_id;
  
  var ruleset_title   = this.evaluation_result.ruleset_title;
  var ruleset_version = this.evaluation_result.ruleset_version;
  var ruleset_id      = this.evaluation_result.ruleset_id;
  
  var eval_title = this.evaluation_result.title;
  var eval_url   = this.evaluation_result.url;
  var date       = this.evaluation_result.date.split(':');
  var eval_time  = date[1] + ":" + date[2];
  var eval_date  = date[0];

  var json = "";
  
  json += prefix + "{";
  
  json += prefix + "  \"group_title\"   : \"" + OpenAjax.a11y.util.escapeForJSON(group_title) + "\",";
  json += prefix + "  \"group_url\"     : \"" + OpenAjax.a11y.util.escapeForJSON(group_url)   + "\",";
  json += prefix + "  \"group_id\"      : \"" + group_id + "\",";
  
  json += prefix + "  \"ruleset_title\"   : \"" + OpenAjax.a11y.util.escapeForJSON(ruleset_title)   + "\",";
  json += prefix + "  \"ruleset_version\" : \"" + OpenAjax.a11y.util.escapeForJSON(ruleset_version) + "\",";
  json += prefix + "  \"ruleset_id\"      : \"" + ruleset_id + "\",";

  json += prefix + "  \"eval_title\"    : \"" + OpenAjax.a11y.util.escapeForJSON(eval_title) + "\",";
  json += prefix + "  \"eval_url\"      : \"" + OpenAjax.a11y.util.escapeForJSON(eval_url)   + "\",";
  json += prefix + "  \"eval_date\"     : \"" + OpenAjax.a11y.util.escapeForJSON(eval_date)  + "\",";
  json += prefix + "  \"eval_time\"     : \"" + OpenAjax.a11y.util.escapeForJSON(eval_time)  + "\",";

  json += prefix + "  \"nodes_filtered\"   : \"" + this.node_results_filtered_out   + "\",";
  json += prefix + "  \"required_rules\"   : \"" + this.group_information.required_rules    + "\",";
  json += prefix + "  \"recommened_rules\" : \"" + this.group_information.recommended_rules + "\",";
  
  json += prefix + "  \"has_results\"      : \"" + this.has_results + "\",";
  json += prefix + "  \"has_rules\"        : \"" + this.has_rules   + "\",";

  var rs = this.node_result_summary;

  json += prefix + "  \"percent\"    : \"" + rs.percent_passed + "\",";
  json += prefix + "  \"passed\"     : \"" + rs.passed         + "\",";
  json += prefix + "  \"violations\" : \"" + rs.violations     + "\",";
  json += prefix + "  \"warnings\"   : \"" + rs.warnings       + "\",";
  json += prefix + "  \"total\"      : \"" + rs.total          + "\",";
  json += prefix + "  \"manual\"     : \"" + rs.manual_checks  + "\",";
  json += prefix + "  \"hidden\"     : \"" + rs.hidden         + "\",";
  
  json += prefix + "  \"rule_results\" : [";
  
  var results     = this.filtered_rule_results;
  var results_len = results.length;
  var comma_len   = results_len - 1;

  for (var i = 0; i < results_len; i++) {
    json += results[i].toJSON(prefix + "  ", flag); 
    if (i < comma_len) json += ",";
  }  
    
  json += prefix + "  ]\n";

  json += "}";

  return json; 

};


/**
 * @method toHTML
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroup
 *
 * @desc Returns an HTML representation of rule category results 
 *
 * @param  {String}  title  -   Title of the report
 *
 * @return  {String}  String representing the HTML for the report 
 */

OpenAjax.a11y.FilteredRuleResultsGroup.prototype.toHTML = function(title) {

  var html = "";
  
  html += "<!DOCTYPE html>\n";
  html += "<html lang='en'>\n";
  html += "  <head>\n";
  html += "    <title>" + title + "</title>\n";
  html += "    <meta charset='ISO-8859-1' />\n";
  html += OpenAjax.a11y.report_css;
  html += "    <script type='text/javascript'>\n";
  html += "      var OAA_REPORT = {};\n";  
  html += "      OAA_REPORT.title = '" + OpenAjax.a11y.util.escapeForJSON(title) + "';\n";  
  html += "      OAA_REPORT.rule_category_data = " + this.toJSON("\n      ") + ";\n\n";
  html += "    </script>\n";
  html += OpenAjax.a11y.report_rule_category_view_js;
  html += "  </head>\n";
  html += OpenAjax.a11y.report_rule_category_view_body;
  html += "</html>\n";
  
  return html;
  
};

/**
 * @method toCSV
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResultsGroup
 *
 * @desc Returns an CSV representation of the filtered cache item results 
 *
 * @param  {String}  title  -   Title of the report
 *
 * @return  {String}  String representing the CSV for the report 
 */

OpenAjax.a11y.FilteredRuleResultsGroup.prototype.toCSV = function(title, header_flag) {

  var eval_title = this.evaluation_result.title;
  var eval_url   = this.evaluation_result.url;
  var date       = this.evaluation_result.date.split(':');
  var eval_time  = date[1] + ":" + date[2];
  var eval_date  = date[0];
  
  if (eval_title.length > 30) eval_title = eval_title.slice(0,27) + "...";
    
//  OpenAjax.a11y.logger.debug("Title: " + title + " Header Flag: " + header_flag + " typeof: " + ((typeof header_flag !== 'boolean') || header_flag));  

  var csv = "";
  
  if ((typeof header_flag !== 'boolean') || header_flag) {
    csv += title + "\n\n";
    csv += "\"OAA ID\"";  
    csv += ",\"Element Description\""; 
    csv += ",\"Element id attribute\""; 
    csv += ",\"Element class attribute\""; 
    csv += ",\"Parent Landmark\""; 
    csv += ",\"Rule ID\"";
    csv += ",\"Type\"";
    csv += ",\"WCAG 2.0 Success Criterion\"";
    csv += ",\"WCAG 2.0 Level\"";
    csv += ",\"Severity\"";
    csv += ",\"Evaluation Result Message\"";
    csv += ",\"Evaluation Date\"";
    csv += ",\"Evaluation Time\"";
    csv += "\"URL Evaluated\"";
    csv += ",\"Title of URL Evaluated\"";
    csv += "\n";
  }  

  var results     = this.filtered_rule_results;
  var results_len = results.length;
  var comma_len   = results_len - 1;

  for (var i = 0; i < results_len; i++) {
                  
     var position = i+1;
            
     var result_item      = results[i];
     var node_results     = result_item.filtered_node_results;
     var node_results_len = node_results.length;
            
     for (var j = 0; j < node_results_len; j++) {
            
       var node_result = node_results[j];
       
       var dom_element = node_result.getDOMElement();

       var cache_item = node_result.getCacheItem();

       csv += "\"" + cache_item.cache_id; 
       csv += "\",\"" + OpenAjax.a11y.util.escapeForJSON(cache_item.toString()); 
       csv += "\",\"" + dom_element.getId(); 
       csv += "\",\"" + dom_element.getClassName(); 
       csv += "\",\"" + dom_element.getParentLandmark(); 
       csv += "\",\"" + node_result.getRuleIdNLS() + ": " + node_result.getRuleSummary();
       csv += "\",\"" + node_result.getRuleRequiredYesNo();
       csv += "\",\"" + node_result.getPrimarySuccessCriterion();
       csv += "\",\"" + node_result.getWCAG20Level();
       csv += "\",\"" + node_result.getResultValue();
       csv += "\",\"" + OpenAjax.a11y.util.escapeForJSON(node_result.getResultMessage());
       csv += "\",\"" + eval_date;
       csv += "\",\"" + eval_time;
       csv += "\",\"" + OpenAjax.a11y.util.escapeForJSON(eval_title);
       csv += "\",\"" + eval_url;
       csv += "\"\n";
               
    }
  }   

  return csv;
};



/*
 * Copyright 2011-2013 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* ---------------------------------------------------------------- */
/*                           FilteredRuleResult                        */
/* ---------------------------------------------------------------- */

/**
 * @constructor FilteredRuleResult
 *
 * @memberOf OpenAjax.a11y
 *
 * @desc Represents one of the rule result in a group of rule results.
 *       The count properties and filtered node results list represent the 
 *       counts and node results that remain after the filter is applied
 *       to the node results for the rule results.
 *          
 * @param  {RuleResult}  rule_result  - Rule result object for rule in a group
 */ 

/**
 * @private
 * @constructor Internal Properties
 *
 * @property {RuleResult}  rule_result           - Rule result object 
 *
 * @property {Array}       filtered_node_results - List of node result objects 
 *                                                 that met filtering conditions
 * @property {Number}  node_results_filtered_out - Number of node results that 
 *                                                 have been filtered from the list
 *
 * @property  {NodeResultSummary}  node_result_summary  - Summary of the node results for 
 *                                                        the filtered rule results  
 *
 * @property  {Array}   messages  - Array of strings with rule result messages
 */

 OpenAjax.a11y.FilteredRuleResult = function(rule_result) {

  this.rule_result = rule_result;
  
  this.cache_id = rule_result.cache_id;
  
  this.filtered_node_results = [];

  this.node_results_filtered_out = 0;
 
  this.node_result_summary = new OpenAjax.a11y.NodeResultSummary();  
  
  this.messages = [];
  
};

/**
 * @method getFilteredNodeResults
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Populate the filtered node results array with the nodes defined by the filter  
 *          
 * @param {Number} filter  -  Number representing the types of results to include in the array
 */

OpenAjax.a11y.FilteredRuleResult.prototype.getFilteredNodeResults = function(filter) {

  function addNodeResultsWebsite(node_results) {  
  
    var node_results_len = node_results.length;
    var count = 0;
    
    for (var i = 0; i < node_results_len; i++) {
      var node_result = node_results[i];
      if (node_result.isScopeWebsite()) {
        var filtered_node_result = new OpenAjax.a11y.FilteredNodeResult(node_result);
        filtered_node_results.push(filtered_node_result);
        count++;
      }  
    }
    
    return count;
  }

  function addNodeResultsPage(node_results) {  
  
    var node_results_len = node_results.length;
    var count = 0;
    
    for (var i = 0; i < node_results_len; i++) {
      var node_result = node_results[i];
      if (node_result.isScopePage()) {
        var filtered_node_result = new OpenAjax.a11y.FilteredNodeResult(node_result);
        filtered_node_results.push(filtered_node_result);
        count++;
      }  
    }
    
    return count;
  }

  function addNodeResultsElement(node_results) {  
  
    var node_results_len = node_results.length;
    var count = 0;
    
    for (var i = 0; i < node_results_len; i++) {
      var node_result = node_results[i];
      if (node_result.isScopeElement()) {
        var filtered_node_result = new OpenAjax.a11y.FilteredNodeResult(node_result);
        filtered_node_results.push(filtered_node_result);
        count++;
      }  
    }
    
    return count;
  }

  function addNodeResults(node_results) {  
  
    var node_results_len = node_results.length;
    
    for (var i = 0; i < node_results_len; i++) {
      var filtered_node_result = new OpenAjax.a11y.FilteredNodeResult(node_results[i]);
      filtered_node_results.push(filtered_node_result);
    }
    return node_results_len;
  }

  var RESULT_FILTER = OpenAjax.a11y.RESULT_FILTER;
  
  var filtered_node_results = this.filtered_node_results;
  var rsf = this.node_result_summary;
  var rr = this.rule_result;

//  if (this.rule_result.isScopeWebsite()) {
//    OpenAjax.a11y.logger.debug("  Rule Result " + this.rule_result.getRuleIdNLS() + " Filter=" + filter + " page=" + RESULT_FILTER.WEBSITE_MANUAL_CHECK + " " + (RESULT_FILTER.WEBSITE_MANUAL_CHECK & filter));  
//  }
  
  if (RESULT_FILTER.VIOLATION            & filter) rsf.addViolations(addNodeResults(rr.node_results_violations));
  if (RESULT_FILTER.WEBSITE_MANUAL_CHECK & filter) rsf.addManualChecks(addNodeResultsWebsite(rr.node_results_manual_checks));
  if (RESULT_FILTER.PAGE_MANUAL_CHECK    & filter) rsf.addManualChecks(addNodeResultsPage(rr.node_results_manual_checks));
  if (RESULT_FILTER.ELEMENT_MANUAL_CHECK & filter) rsf.addManualChecks(addNodeResultsElement(rr.node_results_manual_checks));
  if (RESULT_FILTER.WARNING              & filter) rsf.addWarnings(addNodeResults(rr.node_results_warnings));
  if (RESULT_FILTER.PASS                 & filter) rsf.addPassed(addNodeResults(rr.node_results_passed));
  if (RESULT_FILTER.HIDDEN               & filter) rsf.addHidden(addNodeResults(rr.node_results_hidden));     
  
  var rs = this.rule_result.getNodeResultSummary();
  
  this.node_results_filtered_out = rs.total + rs.manual_checks - rsf.total - rsf.manual_checks;


  // Set filtered node result order property
  
  function byOrder(a,b) {
    if (a.order < b.order) return -1;
    if (a.order > b.order) return 1;
    return 0;
  }
  
  filtered_node_results.sort(byOrder);

  for (var i = 0; i < filtered_node_results.length; i++) {
    filtered_node_results[i].order = (i+1);
  }
  
};

 /**
 * @method hasResults
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Tests if the rule applied to the content in the page 
 *
 * @return {Boolean} True if the application of the rule has results
 */
 
OpenAjax.a11y.FilteredRuleResult.prototype.hasResults = function () {

   return this.node_result_summary.hasResults();

};

 /**
 * @method getNodeResultSummary
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Gets numerical summary information about the node results 
 *
 * @return {NodeResultSummary} Returns the NodeResultSummary object 
 */
OpenAjax.a11y.FilteredRuleResult.prototype.getNodeResultSummary = function () {

  return this.node_result_summary;

};


 /**
 * @method getRuleResult
 * @deprecated
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Gets the worst node result value from the node results  
 *
 * @return {RESULT_VALUE} Returns a result value constant 
 */
OpenAjax.a11y.FilteredRuleResult.prototype.getRuleResult = function () {

  return this.getRuleResultValue();

};

 /**
 * @method getRuleResultValue
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Gets the worst node result value from the node results  
 *
 * @return {RESULT_VALUE} Returns a result value constant 
 */
OpenAjax.a11y.FilteredRuleResult.prototype.getRuleResultValue = function () {

  var RESULT_VALUE = OpenAjax.a11y.RESULT_VALUE;
  var nrs = this.node_result_summary;

  if (nrs.violations)         return RESULT_VALUE.VIOLATION;
  else if (nrs.warnings)      return RESULT_VALUE.WARNING;
  else if (nrs.manual_checks) return RESULT_VALUE.MANUAL_CHECK;
  else if (nrs.passed)        return RESULT_VALUE.PASS;
  else if (nrs.hidden)        return RESULT_VALUE.HIDDEN;
 
  return RESULT_VALUE.NONE;

};


/**
 * @method getResultMessages
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Returns an NLS string representing the evaluation result message for the rule  
 *
 * @return {Array} An array of strings with rule result messages (typically only one string)
 */

OpenAjax.a11y.FilteredRuleResult.prototype.getResultMessages = function () {

  if ((typeof this.messages !== 'object') || 
      (this.messages.length === 0)) {
    
    this.messages = this.rule_result.getResultMessages(this.node_result_summary);
  }  
  
  return this.messages;
  
};

/**
 * @method getResultMessage
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Returns an NLS string representing the evaluation result message for the rule  
 *
 * @return {String} Returns a NLS string 
 */

OpenAjax.a11y.FilteredRuleResult.prototype.getResultMessage = function () {
  
  return this.rule_result.getResultMessage(this.node_result_summary);
  
};

/**
 * @method getNodeResults
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Returns an array of node results in severity order 
 *
 * @return {Array} Returns a array of NodeResult objects
 */

OpenAjax.a11y.FilteredRuleResult.prototype.getNodeResults = function () {
 
  return this.filtered_node_results;
  
};

/**
 * @method isRuleEnabled
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Tests whether the rule is enabled or disabled for evaluation  
 *
 * @param {Boolean}  True if rule is enabled, false if rule disabled
 */

OpenAjax.a11y.FilteredRuleResult.prototype.isRuleEnabled = function () {

  return this.rule_result.isRuleEnabled();

};

/**
 * @method isRuleRequired
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Tests whether the rule is mapped as a required or recommended rule  
 *
 * @param {Boolean}  True if rule is a required rule, false if a recommended rule
 */

OpenAjax.a11y.FilteredRuleResult.prototype.isRuleRequired = function () {

  return this.rule_result.isRuleRequired();

};

/**
 * @method getRuleRequiredYesNo
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Returns 'Yes' or "No' depending on whether the rule is required or recommended rule  
 *
 * @param {String} Returns "Yes" if required, otherwise "No" 
 */

OpenAjax.a11y.FilteredRuleResult.prototype.getRuleRequiredYesNo = function () {

  return this.rule_result.getRuleRequiredYesNo();
  
};

/**
 * @method getRuleRequiredOrRecommended
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Returns 'Required' or "Recommended' depending on whether the rule is required or recommended rule  
 *
 * @param {String} Returns "Required" if required, otherwise "Recommended" 
 */

OpenAjax.a11y.FilteredRuleResult.prototype.getRuleRequiredOrRecommended = function () {

  return this.rule_result.getRuleRequiredOrRecommended();
  
};


/**
 * @method getRuleId
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Returns the id of the rule associated with this rule result
 * 
 * @return {String} String representing the rule id
 */

OpenAjax.a11y.FilteredRuleResult.prototype.getRuleId = function () {

  return this.rule_result.getRuleId();
   
};

/**
 * @method getRuleIdNLS
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Returns the nls id of the rule associated with this rule result
 * 
 * @return {String} String representing the nls rule id
 */

OpenAjax.a11y.FilteredRuleResult.prototype.getRuleIdNLS = function () {

  return this.rule_result.getRuleIdNLS();
   
};

/**
 * @method getRuleCategory
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Get a localized title, url and description of the rule category
 * 
 * @return {Object}  Returns an object with the following propertues:<br/>
 *                   'title':  String representing the Title of the rule category <br/>
 *                   'desc':   String providing a longer description of the rule category<br/>
 *                   'url':    URL to more information about the rule category (maybe blank)<br/>
 */

OpenAjax.a11y.FilteredRuleResult.prototype.getRuleCategory = function () {

  return this.rule_result.getRuleCategory();
  
};

/**
 * @method getRuleCategoryConstant
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Returns the numeric value for the rule category
 * 
 * @return {Number}  Numeric value of the rule category
 */

OpenAjax.a11y.FilteredRuleResult.prototype.getRuleCategoryConstant = function () {

  return this.rule_result.getRuleCategoryConstant();
  
};


/**
 * @method getRuleScope
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Get the rule scope constant of the rule 
 *
 * @return {Number} rule scope constant
 */
 
OpenAjax.a11y.FilteredRuleResult.prototype.getRuleScope = function () {

  return this.rule_result.getRuleScope();
   
};

/**
 * @method getRuleScopeNLS
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Get a localized string of the rule scope (i.e. 'element' or 'page')
 *
 * @return {String} Localized string of the rule scope
 */
 
OpenAjax.a11y.FilteredRuleResult.prototype.getRuleScopeNLS = function () {

  return this.rule_result.getRuleScopeNLS();
   
};

/**
 * @method isScopeWebsite
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Test if the rule is a website level rule
 *
 * @return {Boolean} True if the rule has a scope of website, otherwise false
 */
 
OpenAjax.a11y.FilteredRuleResult.prototype.isScopeWebsite = function () {

  return this.rule_scope === OpenAjax.a11y.RULE_SCOPE.WEBSITE;
  
};

/**
 * @method isScopePage
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Returns an localized string of the rule scope (i.e. element or page)
 *
 * @return {Boolean} True if the rule has a scope of page, otherwise false
 */
 
OpenAjax.a11y.FilteredRuleResult.prototype.isScopePage = function () {

  return this.rule_result.isScopePage();
  
};

/**
 * @method isScopeElement
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Returns an localized string of the rule scope (i.e. element or page)
 *
 * @return {Boolean} True if the rule has a scope of element, otherwise false
 */
 
OpenAjax.a11y.FilteredRuleResult.prototype.isScopeElement = function () {

  return this.rule_result.isScopeElement();
  
};



/**
 * @method getRuleDefinition
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Gets the definition of the rule
 *
 * @return {String} Localized string of the rule definition based on being 
 *                  required or recommended
 */

OpenAjax.a11y.FilteredRuleResult.prototype.getRuleDefinition = function () {

  return this.rule_result.getRuleDefinition();
  
};




/**
 * @method getRuleSummary
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Gets the summary of the rule
 *
 * @return {String} Localized string of the rule summary based on being 
 *                  required or recommended
 */

OpenAjax.a11y.FilteredRuleResult.prototype.getRuleSummary = function () {

  return this.rule_result.getRuleSummary();
  
};

/**
 * @method getPurpose
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Gets an array strings representing the purpose, basically
 *       how does the rule help people with disabilities
 *
 * @return  {Array}  Returns an array of localized string describing the purpose
 */
OpenAjax.a11y.FilteredRuleResult.prototype.getPurpose = function () {

  return this.rule_result.getPurpose();
  
};

/**
 * @method getTargetResourcesDescription
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Get a description of the markup or page feature the rule is evaluates
 *
 * @return  {String}  Localized string representing the markup or page feature 
 *                    tested by the rule
 */
OpenAjax.a11y.FilteredRuleResult.prototype.getTargetResourcesDescription = function () {

  return this.rule_result.getTargetResourcesDescription();

};

/**
 * @method getTargetResources
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Returns an localized array strings representing target resources of 
 *       the rule
 *
 * @return  {Array}  Returns an array of strings identifying the elements and/or
 *                    attributes that the rule evaluates
 */
OpenAjax.a11y.FilteredRuleResult.prototype.getTargetResources = function () {

  return this.rule_result.getTargetResources();

};



/**
 * @method getTechniques
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 * @desc Get the techniques to implement the requirements of the rule 
 *
 * @return  {Array}  Returns an array of localized strings
 */
 
OpenAjax.a11y.FilteredRuleResult.prototype.getTechniques = function () {

  return this.rule_result.getTechniques();
  
};

/**
 * @method getManualCheckProcedures
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Gets manual checking proceedures for evaluating the rule
 *       requirements
 *
 * @return  {Array}  Returns an array of objects with localized strings and urls.<br/>
 *                   Each object has the following properties:<br/>
 *                   'title' : Localized string describing the technique<br/> 
 *                   'url': URL to more information about the technique<br/>
 */
OpenAjax.a11y.FilteredRuleResult.prototype.getManualCheckProcedures = function () {

  return this.rule_result.getManualCheckProcedures();

};



/**
 * @method getInformationalLinks
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Get information links related to understanding or implementation of the rule
 *
 * @return  {Array}  Returns an array of objects, each object includes the following properties:<br/>
 *                   'type_const' : Number representing the type of information,<br/> 
 *                   'title'      : Title descriping the type of information, <br/>
 *                   'url'        : Link to more information <br/>
 *
 * @example
 *
 * var node_list = [];
 * var info_links = rule.getInformationalLinks();
 * 
 * for(var i = 0; i < info_links.length; i++) {
 *   var info_link = info_links[i];
 *
 *   // Using object properties to create a link element
 *   var node = document.createElement('a');
 *   node.appendChild(document.createTextNode(info_link.title));
 *   node.setAttribute('href',  info_link.url);
 *   node.setAttribute('class', info_link.type_const.toString());
 *
 *   node_list.push(node);
 * }
 */

OpenAjax.a11y.FilteredRuleResult.prototype.getInformationalLinks = function () {

  return this.rule_result.getInformationalLinks();

};

/**
 * @method getPrimarySuccessCriterion
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Get information about primary WCAG 2.0 Success Criteria for the rule
 *
 * @return  {Object}  Object representing the success criteria, 
 *                    each object has the following properties:<br/> 
 *                    'id'           : A "P.G.SC" formatted string representing the SC,<br/>  
 *                    'title'        : A localized title for the SC,<br/>
 *                    'description'  : A localized description of the SC,<br/>
 *                    'url'          : A url to the SC in the WCAG 2.0 document<br/>
 *                    'level'        : The level of the Success Criterion (e.g. A, AA or AAA)<br/>
 *
 * @example
 *
 * var sc_info = rule.getPrimarySuccessCriterion();
 * 
 * // Creating a link element to the primary success criterion
 * var node = document.createElement('a');
 * node.appendChild(document.createTextNode(sc_info.id + " " + sc_info.title));
 * node.setAttribute('href',  sc_info.url);
 * node.setAttribute('title', sc_info.description);
 * } 
 */

OpenAjax.a11y.FilteredRuleResult.prototype.getPrimarySuccessCriterion = function () {

  return this.rule_result.getPrimarySuccessCriterion();

};

/**
 * @method getRelatedSuccessCriteria
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Get information about the related WCAG 2.0 Success Criteria for the rule
 *
 * @return  {Array}  Array of objects representing the success criteria, each object has the 
 *                   following properties: <br/>
 *                   'id'           : A "P.G.SC" formatted string representing the SC, <br/> 
 *                   'title'        : A localized title for the SC,<br/>
 *                   'description'  : A localized description of the SC,<br/>
 *                   'url'          : A url to the SC in the WCAG 2.0 document<br/>
 *                   'level'        : The level of the Success Criterion (e.g. A, AA or AAA)<br/>
 */

OpenAjax.a11y.FilteredRuleResult.prototype.getRelatedSuccessCriteria = function () {

  return this.rule_result.getRelatedSuccessCriteria();

};

/**
 * @method getWCAG20LevelConstant
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Get the numerical constant for the WCAG 2.0 Success Criterion Level 
 *       based on the primary id of the rule
 *
 * @return  {Number}  Number representing the WCAG 2.0 level 
 */

OpenAjax.a11y.FilteredRuleResult.prototype.getWCAG20LevelConstant = function () {

  return this.rule_result.getWCAG20LevelConstant();

};


/**
 * @method getWCAG20Level
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Get the string representation of the the WCAG 2.0 Success Criterion Level 
 *       based on the primary id of the rule
 *
 * @return  {String}  String representing the WCAG 2.0 success criterion level 
 *                    (i.e. A, AA or AAA)
 */

OpenAjax.a11y.FilteredRuleResult.prototype.getWCAG20Level = function () {

  return this.rule_result.getWCAG20Level();

};

/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Creates a text string representation of the filtered rule result object 
 *
 * @return {String} Returns a text string representation of the filtered rule result object
 */

OpenAjax.a11y.FilteredRuleResult.prototype.toString = function () {

 var str = this.getRuleDefinition() + " (";
 
 if (this.has_results) {
   if (this.node_result_summary.passed || this.node_result_summary.failures) str += this.node_result_summary.percent_passed + " percent passed";
   if (this.node_result_summary.manual_checks) str += " " + this.node_result_summary.manual_checks + " manual checks";
 }
 else {
   str += "no results";
 }  

 str += ")"; 

 return str;
 
};

/**
 * @method toJSON
 *
 * @memberOf OpenAjax.a11y.FilteredRuleResult
 *
 * @desc Returns a JSON representation of the cache item 
 *          
 * @param {String}  prefix  -  A prefix string typically spaces
 * @param {Boolean} flag    -  if true include node result details
 *
 * @return  {String}  String representing the cache item result object
 */

OpenAjax.a11y.FilteredRuleResult.prototype.toJSON = function(prefix, flag) {

  if (typeof flag !== 'boolean') flag = true;

  var next_prefix = "";
  var next_prefix_2 = "";

  if (typeof prefix !== 'string' || prefix.length === 0) prefix = "";
  else {
    next_prefix = prefix + "  ";  
    next_prefix_2 = next_prefix + "  ";  
  }  

  var i;
  var json = "";
  var rs = this.node_result_summary;
  
  var escapeForJSON = OpenAjax.a11y.util.escapeForJSON;
  
  json += prefix + "{ \"rule_id\"         : \"" + this.rule_result.getRuleId() + "\",";
  json += prefix + "  \"definition\"      : \"" + escapeForJSON(this.rule_result.getRuleDefinition()) + "\",";
  json += prefix + "  \"summary\"         : \"" + escapeForJSON(this.rule_result.getRuleSummary())    + "\",";
  json += prefix + "  \"nls_rule_id\"     : \"" + this.rule_result.getRuleIdNLS()         + "\",";
  json += prefix + "  \"required\"        : "   + this.rule_result.isRuleRequired() + ",";
  json += prefix + "  \"wcag_level\"      : \"" + this.rule_result.getWCAG20Level()       + "\",";
  json += prefix + "  \"wcag_primary_id\" : \"" + escapeForJSON(this.rule_result.getPrimarySuccessCriterion()) + "\",";

  json += prefix + "  \"has_results\"   : "  + this.hasResults()  + ",";

  json += prefix + "  \"percent\"       : "  + rs.percent_passed + ",";
  json += prefix + "  \"passed\"        : "  + rs.passed         + ",";
  json += prefix + "  \"violations\"    : "  + rs.violations     + ",";
  json += prefix + "  \"warnings\"      : "  + rs.warnings       + ",";
  json += prefix + "  \"failures\"      : "  + rs.failures       + ",";
  json += prefix + "  \"total\"         : "  + rs.total          + ",";
  json += prefix + "  \"manual\"        : "  + rs.manual_checks  + ",";
  json += prefix + "  \"hidden\"        : "  + rs.hidden         + ",";
  
  
  if (flag) {
    json += prefix + "  \"filtered\"      : "  + this.node_results_filtered_out  + ",";

    var node_results     = this.getNodeResults();
    var node_results_len = node_results.length;
    var comma_count      = node_results_len - 1;
  
    json += prefix + "  \"node_results\" : [";
    for (i = 0; i < node_results_len; i++) {
      json += this.filtered_node_results[i].toJSON(next_prefix);
      if (i < comma_count) json += ",";
    }  
    json += prefix + "  ]";
  }  
  
  json += prefix + "}";

  return json;

};


/*
 * Copyright 2011-2013 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


/* ---------------------------------------------------------------- */
/*                     FilteredNodeResult                           */
/* ---------------------------------------------------------------- */

/**
 * @constructor FilteredNodeResult
 *
 * @memberOf OpenAjax.a11y
 *
 * @desc Constructor for an object that contains a the results of 
 *       the evaluation of a rule on a dom node and is part of a ordered list
 *
 * @param  {NodeResult} node_result             - reference to the rule result object
 */ 

/**
 * @private
 * @constructor Internal Properties
 *
 * @property  {NodeResult} node_result         - reference to the node result object
 * @property  {Number}     order               - Number representing the ordinal 
 *                                               position of the node in the DOM 
 *                                               normalize for this list
 */

OpenAjax.a11y.FilteredNodeResult = function (node_result) {

  this.node_result = node_result;
 
  this.order  = node_result.getDOMElement().document_order;
  
};

 /**
 * @method getResultMessage
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Returns an localized node result message 
 *
 * @return {String} String with node result message
 */
OpenAjax.a11y.FilteredNodeResult.prototype.getResultMessage = function () {

  return this.node_result.getResultMessage();
  
};


/**
 * @method getTargetResourceProperties
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Get the DOM cache values for the properties associated with a rule
 * 
 * @return {Array} Array of objects with the following properties:<br/>
 *                 'label'       : String label of the property<br/>
 *                 'value'       : String value of the property<br/>
 *                 'description' : String providing additional information about the property <br/>
 */

OpenAjax.a11y.FilteredNodeResult.prototype.getTargetResourceProperties = function () {

  return this.node_result.getTargetResourceProperties();

};



/**
 * @method getResultValue
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Gets the numerical value of the result value of the result 
 *
 * @return {Number} Returns a number representing the result value of the result
 */

OpenAjax.a11y.FilteredNodeResult.prototype.getResultValue = function () {

  return this.node_result.getResultValue();
 
};

/**
 * @method getResultValueNLS
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Gets string value for result value abbreviation 
 *
 * @return {Object} Returns a string value of the result value
 */

OpenAjax.a11y.FilteredNodeResult.prototype.getResultValueNLS = function () {

  return this.node_result.getResultValueNLS();
 
};


/**
 * @method getResultValueStringsNLS
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Gets result value label, abbreviation, description and style 
 *
 * @return {Object} Returns a object with the following properties: <br/>
 *                  'label'       : String representing the result value <br/>
 *                  'abbrev'      : String representing the abbreviation of the result value<br/>
 *                  'description' : String describing the result value, may be useful as tooltip<br/>
 *                  'style'       : String that can used as a CSS selector for styling results<br/> 
 */

OpenAjax.a11y.FilteredNodeResult.prototype.getResultValueStringsNLS = function () {

  return this.node_result.getResultValueStringsNLS();
 
};
/**
 * @method getOrdinalPosition
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Returns a the ordinal position of the element in a list of node results
 * 
 * @return {Number} Returns a number indicating the position in a list of node results
 */

OpenAjax.a11y.FilteredNodeResult.prototype.getOrdinalPosition = function () {

  return this.order;

};

/**
 * @method isRuleEnabled
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Tests whether the rule is enabled or disabled for evaluation  
 *
 * @param {Boolean}  True if rule is enabled, false if rule disabled
 */

OpenAjax.a11y.FilteredNodeResult.prototype.isRuleEnabled = function () {

  return this.node_result.isRuleEnabled();

};

/**
 * @method isRuleRequired
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Tests whether the rule is mapped as a required or recommended rule  
 *
 * @param {Boolean}  True if rule is a required rule, false if a recommended rule
 */

OpenAjax.a11y.FilteredNodeResult.prototype.isRuleRequired = function () {

  return this.node_result.isRuleRequired();

};


/**
 * @method getRuleRequiredYesNo
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Returns 'Yes' or "No' depending on whether the rule is required or recommended rule  
 *
 * @param {String} Returns "Yes" if required, otherwise "No" 
 */

OpenAjax.a11y.FilteredNodeResult.prototype.getRuleRequiredYesNo = function () {

  return this.node_result.getRuleRequiredYesNo();
  
};

/**
 * @method getRuleRequiredOrRecommended
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Returns 'Required' or "Recommended' depending on whether the rule is required or recommended rule  
 *
 * @param {String} Returns "Required" if required, otherwise "Recommended" 
 */

OpenAjax.a11y.FilteredNodeResult.prototype.getRuleRequiredOrRecommended = function () {

  return this.node_result.getRuleRequiredOrRecommended();
  
};



/**
 * @method getRuleId
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Get the programmatic id that uniquely identifies the rule
 *
 * @return {String} The rule id
 */

OpenAjax.a11y.FilteredNodeResult.prototype.getRuleId = function () {

  return this.node_result.getRuleId();
   
};

/**
 * @method getRuleIdNLS
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Get a localized human readable id for uniquely identifying the rule
 *
 * @return {String} Localized string of the rule id
 */

OpenAjax.a11y.FilteredNodeResult.prototype.getRuleIdNLS = function () {

  return this.node_result.getRuleIdNLS();
   
};



/**
 * @method getRuleCategory
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Get a localized title, url and description of the rule category
 * 
 * @return {Object}  Returns an object with the following propertues:<br/>
 *                   'title':  String representing the Title of the rule category <br/>
 *                   'desc':   String providing a longer description of the rule category<br/>
 *                   'url':    URL to more information about the rule category (maybe blank)<br/>
 */

OpenAjax.a11y.FilteredNodeResult.prototype.getRuleCategory = function () {

  return this.node_result.getRuleCategory();
  
};


/**
 * @method getRuleCategoryConstant
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Returns the numeric value for the rule category
 * 
 * @return {Number}  Numeric value of the rule category
 */

OpenAjax.a11y.FilteredNodeResult.prototype.getRuleCategoryConstant = function () {

  return this.node_result.getRuleCategoryConstant();
  
};

/**
 * @method getRuleScope
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Get the rule scope constant of the rule 
 *
 * @return {Number} rule scope constant
 */
 
OpenAjax.a11y.FilteredNodeResult.prototype.getRuleScope = function () {

  return this.node_result.getRuleScope(); 
  
};

/**
 * @method getRuleScopeNLS
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Get a localized string of the rule scope (i.e. 'element' or 'page')
 *
 * @return {String} Localized string of the rule scope
 */
 
OpenAjax.a11y.FilteredNodeResult.prototype.getRuleScopeNLS = function () {

  return this.node_result.getRuleScopeNLS(); 
  
};


/**
 * @method isScopeWebsite
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Test if the rule is a website level rule
 *
 * @return {Boolean} True if the rule has a scope of website, otherwise false
 */
 
OpenAjax.a11y.FilteredNodeResult.prototype.isScopeWebsite = function () {

  return tthis.node_result.isScopeWebsite();
  
};

/**
 * @method isScopePage
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Returns an localized string of the rule scope (i.e. element or page)
 *
 * @return {Boolean} True if the rule has a scope of page, otherwise false
 */
 
OpenAjax.a11y.FilteredNodeResult.prototype.isScopePage = function () {

  return this.node_result.isScopePage(); 
  
};

/**
 * @method isScopeElement
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Returns an localized string of the rule scope (i.e. element or page)
 *
 * @return {Boolean} True if the rule has a scope of element, otherwise false
 */
 
OpenAjax.a11y.FilteredNodeResult.prototype.isScopeElement = function () {

  return this.node_result.isScopeElement(); 
  
};

/**
 * @method getRuleDefinition
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Gets the definition of the rule
 *
 * @return {String} Localized string of the rule definition based on being 
 *                  required or recommended
 */

OpenAjax.a11y.FilteredNodeResult.prototype.getRuleDefinition = function () {

  return this.node_result.getRuleDefinition();
  
};

/**
 * @method getRuleSummary
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc  Gets the summary of the rule
 *
 * @return {String} Localized string of the rule summary based on being 
 *                  required or recommended
 */

OpenAjax.a11y.FilteredNodeResult.prototype.getRuleSummary = function () {

  return this.node_result.getRuleSummary();
  
};

/**
 * @method getPurpose
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Gets an array strings representing the purpose, basically
 *       how does the rule help people with disabilities
 *
 * @return  {Array}  Returns an array of localized string describing the purpose
 */
OpenAjax.a11y.FilteredNodeResult.prototype.getPurpose = function () {

  return this.node_result.getPurpose();

};


/**
 * @method getTargetResourcesDescription
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Get a description of the markup or page feature the rule is evaluates
 *
 * @return  {String}  Localized string representing the markup or page feature 
 *                    tested by the rule
 */
OpenAjax.a11y.FilteredNodeResult.prototype.getTargetResourcesDescription = function () {

  return this.node_result.getTargetResourcesDescription();

};

/**
 * @method getTargetResources
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Returns an localized array strings representing target resources of 
 *       the rule
 *
 * @return  {Array}  Returns an array of strings identifying the elements and/or
 *                    attributes that the rule evaluates
 */
OpenAjax.a11y.FilteredNodeResult.prototype.getTargetResources = function () {

  return this.node_result.getTargetResources();

};


/**
 * @method getTechniques
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Get the techniques to implement the requirements of the rule 
 *
 * @return  {Array}  Returns an array of objects with localized strings and urls.<br/>
 *                   Each object has the following properties:<br/>
 *                   'title' : Localized string describing the technique<br/> 
 *                   'url': URL to more information about the technique<br/>
 */
 
OpenAjax.a11y.FilteredNodeResult.prototype.getTechniques = function () {

  return this.node_result.getTechniques();
  
};

/**
 * @method getManualCheckProcedures
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Gets manual checking proceedures for evaluating the rule
 *       requirements
 *
 * @return  {Array}  Returns an array of objects with localized strings and urls.<br/>
 *                   Each object has the following properties:<br/>
 *                   'title' : Localized string describing the technique<br/> 
 *                   'url': URL to more information about the technique<br/>
 */
 
OpenAjax.a11y.FilteredNodeResult.prototype.getManualCheckProcedures = function () {

  return this.node_result.getManualCheckProcedures();

};

/**
 * @method getInformationalLinks
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Get information links related to understanding or implementation of the rule
 *
 * @return  {Array}  Returns an array of objects, each object includes the following properties:<br/>
 *                   'type_const' : Number representing the type of information,<br/> 
 *                   'title'      : Title descriping the type of information, <br/>
 *                   'url'        : Link to more information <br/>
 *
 * @example
 *
 * var node_list = [];
 * var info_links = rule.getInformationalLinks();
 * 
 * for(var i = 0; i < info_links.length; i++) {
 *   var info_link = info_links[i];
 *
 *   // Using object properties to create a link element
 *   var node = document.createElement('a');
 *   node.appendChild(document.createTextNode(info_link.title));
 *   node.setAttribute('href',  info_link.url);
 *   node.setAttribute('class', info_link.type_const.toString());
 *
 *   node_list.push(node);
 * }
 */
OpenAjax.a11y.FilteredNodeResult.prototype.getInformationalLinks = function () {

  return this.node_result.getInformationalLinks();

};



/**
 * @method getPrimarySuccessCriterion
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Get information about primary WCAG 2.0 Success Criteria for the rule
 *
 * @return  {Object}  Object representing the success criteria, 
 *                    each object has the following properties:<br/> 
 *                    'id'           : A "P.G.SC" formatted string representing the SC,<br/>  
 *                    'title'        : A localized title for the SC,<br/>
 *                    'description'  : A localized description of the SC,<br/>
 *                    'url'          : A url to the SC in the WCAG 2.0 document<br/>
 *                    'level'        : The level of the Success Criterion (e.g. A, AA or AAA)<br/>
 * @example
 *
 * var sc_info = rule.getPrimarySuccessCriterion();
 * 
 * // Creating a link element to the primary success criterion
 * var node = document.createElement('a');
 * node.appendChild(document.createTextNode(sc_info.id + " " + sc_info.title));
 * node.setAttribute('href',  sc_info.url);
 * node.setAttribute('title', sc_info.description);
 * } 
 */

OpenAjax.a11y.FilteredNodeResult.prototype.getPrimarySuccessCriterion = function () {

  return this.node_result.getPrimarySuccessCriterion();

};

/**
 * @method getRelatedSuccessCriteria
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Get information about the related WCAG 2.0 Success Criteria for the rule
 *
 * @return  {Array}  Array of objects representing the success criteria, each object has the 
 *                   following properties: <br/>
 *                   'id'           : A "P.G.SC" formatted string representing the SC, <br/> 
 *                   'title'        : A localized title for the SC,<br/>
 *                   'description'  : A localized description of the SC,<br/>
 *                   'url'          : A url to the SC in the WCAG 2.0 document<br/>
 *                   'level'        : The level of the Success Criterion (e.g. A, AA or AAA)<br/>
 */

OpenAjax.a11y.FilteredNodeResult.prototype.getRelatedSuccessCriteria = function () {

  return this.node_result.getRelatedSuccessCriteria();

};

/**
 * @method getWCAG20LevelConstant
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Get the numerical constant for the WCAG 2.0 Success Criterion Level 
 *       based on the primary id of the rule
 *
 * @return  {Number}  Number representing the WCAG 2.0 level 
 */

OpenAjax.a11y.FilteredNodeResult.prototype.getWCAG20LevelConstant = function () {

  return this.node_result.getWCAG20LevelConstant();

};

/**
 * @method getWCAG20Level
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Get the string representation of the the WCAG 2.0 Success Criterion Level 
 *       based on the primary id of the rule
 *
 * @return  {String}  String representing the WCAG 2.0 success criterion level 
 *                    (i.e. A, AA or AAA)
 */

OpenAjax.a11y.FilteredNodeResult.prototype.getWCAG20Level = function () {

  return this.node_result.getWCAG20Level();

};

/**
 * @method getXPath
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Returns the xpath of the associated element
 * 
 * @return {String} information about the node result 
 */

OpenAjax.a11y.FilteredNodeResult.prototype.getXPath = function () {
  
  return this.node_result.getXPath();
 
};

/**
 * @method getDOMElement
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Returns the dom element object
 *
 * @return {String} Returns a dom element associated with the cache item
 */

OpenAjax.a11y.FilteredNodeResult.prototype.getDOMElement = function () {

  return this.node_result.getDOMElement();
  
};



/**
 * @method getCacheItem
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Gets the cache item associated with the node result
 *
 * @return {Object} Returns a cache item object
 */

OpenAjax.a11y.FilteredNodeResult.prototype.getCacheItem = function () {

  return this.node_result.getCacheItem();
 
};


/**
 * @method toString
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Creates a text string representation of the node result object 
 *
 * @return {String} Returns a text string representation of the node result object
 */

OpenAjax.a11y.FilteredNodeResult.prototype.toString = function () {

  return this.node_result.toString();
  
};

/**
 * @method toJSON
 *
 * @memberOf OpenAjax.a11y.FilteredNodeResult
 *
 * @desc Creates JSON object descibing the properties of the node result
 *
 * @param {String} prefix  -  A prefix string typically spaces
 * 
 * @return String information about the node result 
 */

OpenAjax.a11y.FilteredNodeResult.prototype.toJSON = function (prefix) {

  return this.node_result.toJSON();
  
};

/*
 * Copyright 2011-2013 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* -------------------------------------------------------------------- */
/*     Formats Filtered Rule Results Groups objects                     */
/*     for Tree View Rendering                                          */
/* -------------------------------------------------------------------- */

/** 
 * @namespace OpenAjax.a11y.formatters
 */

OpenAjax.a11y.formatters = OpenAjax.a11y.formatters || {};

/**
 * @constructor TreeViewOfFilteredRuleResultsGroups
 *
 * @memberOf OpenAjax.a11y.formatters
 *
 * @desc Constructs a list of rule groups results that can be used by tree views 
 *       (i.e. XUL custom tree views)
 *
 * @param  {FilteredRuleResultsGroups}  filtered_rule_results_groups  - Filtered rule
 *                                                                      results groups 
 *                                                                      object 
 *
 * @param  {Number}  level - level of this list in the tree structure 
 *
 * 
 * @property  {FilteredRuleResultsGroups}  filtered_rule_results_groups  - Filtered rule
 *                                                                         results groups 
 *                                                                         object reference
 *
 * @property  {Array}  rule_result_items  - List of rule result itmes with properties 
 *                                          optimized for tree view or list rendering   
 */

OpenAjax.a11y.formatters.TreeViewOfFilteredRuleResultsGroups = function(filtered_rule_results_groups, level) {

  var cache_nls = OpenAjax.a11y.cache_nls.getCacheNLS();
  var boolean_values = cache_nls.boolean_values;

  if (typeof level !== 'number') level = 0;
  
  // Initialize object properties
  this.filtered_rule_results_groups = filtered_rule_results_groups;  
  this.rule_result_items = [];
  
  var frr_groups     = filtered_rule_results_groups.filtered_rule_results_groups;
  var frr_groups_len = frr_groups.length;

  var rrg_item = null;

  for (var i = 0; i < frr_groups_len; i++) {
  
    var frrg =  frr_groups[i];
    
    var rs = frrg.getNodeResultSummary();
    var has_results = rs.hasResults();
    
    var failures_prop = "zero";

    if (rs.warnings   > 0)  failures_prop  = "warning";
    if (rs.violations > 0)  failures_prop  = "violation";
    
    var number_of_rules = frrg.getNumberOfRules();
    
    var summary = frrg.title + " (";    
    if (number_of_rules === 1) summary += "1 " + cache_nls.rule + ")";
    else summary += number_of_rules + " " + cache_nls.rules + ")";

    // Add information about number of rules
    var tv_frrg;
    if (typeof frrg.filtered_rule_results_groups === 'undefined') {
      tv_frrg = new OpenAjax.a11y.formatters.TreeViewOfFilteredRuleResultsGroup(frrg, (level+1));   
    } 
    else {
      tv_frrg = new OpenAjax.a11y.formatters.TreeViewOfFilteredRuleResultsGroups(frrg, (level+1)); 
    }

    var children = tv_frrg.getOpenRuleResults();
    
    var has_rules   = frrg.hasRules();

    rrg_item = {
      // properties for tree view
      container : true,
      open      : true,
      level     : level,
      type      : 'FILTERED_RULE_GROUP_RESULT',
      
      // Filtered rule result properties
      
      filtered_rule_results_group: frrg,
      
      tree_view    : tv_frrg,
      
      children     : children,

      group_id     : frrg.group_id,

      summary      : summary,
      summary_prop : "group" + level,
      
      definition   : summary,
      message      : "",

      wcag20_level       : 0,
      wcag20_level_label : "",
      wcag20_level_prop  : "",

      required       : null,      
      required_label : "",
      required_prop  : "",

      has_results          : has_results,
      
      percent_passed       : rs.percent_passed,
      percent_passed_label : has_results ? rs.percent_passed.toString() : '-',
      percent_passed_prop  : (rs.percent_passed < 1) ? 'zero' : 'passed',

      passed_count         : rs.passed,
      passed_label         : has_results ? rs.passed.toString() : '-',
      passed_prop          : (rs.passed >= 1) ? 'passed' : 'zero',

      violations_count     : rs.violations,
      violations_label     : has_results ? rs.violations.toString() : '-',
      violations_prop      : (rs.violations >= 1) ? 'violation' : 'zero',
      
      warnings_count       : rs.warnings,
      warnings_label       : has_results ? rs.warnings.toString() : '-',
      warnings_prop        : (rs.warnings >= 1) ? 'warning' : 'zero',
      
      failures_count       : rs.failures,
      failures_label       : has_results ? rs.failures.toString() : '-',
      failures_prop        : (rs.failures >= 1) ? failures_prop : 'zero',
            
      manual_checks_count  : rs.manual_checks,
      manual_checks_label  : has_results ? rs.manual_checks.toString() : '-',
      manual_checks_prop   : (rs.manual_checks >= 1) ? 'manual_check' : 'zero',      
      
      hidden_count         : rs.hidden,
      hidden_label         : has_results ? rs.hidden.toString() : '-',
      hidden_prop          : (rs.hidden >= 1) ? 'hidden' : 'zero'
    
    };

    this.rule_result_items.push(rrg_item);
   
  }

};


/**
 * @method setOpenState
 *
 * @memberOf OpenAjax.a11y.formatters.TreeViewOfFilteredRuleResultsGroups
 *
 * @desc Sets open state on all container items in the list
 *       Used for expanding or collapsing all leafs in a tree
 *
 * @param  {Boolean} flag  -  If true all container elements will be set 
 *                            to open, otherwise containers element set 
 *                            to close  
 */

OpenAjax.a11y.formatters.TreeViewOfFilteredRuleResultsGroups.prototype.setOpenState = function (flag) {

   function setContainers(list) {
   
     for (var i = 0; i < list.length; i++) {
   
        var item = list[i];
        
        if (item.container) {
          item.open = flag;
          
          if (item.children && item.children.length) setContainers(item.children);
        }     
     }
   }
   
   if (typeof flag !== 'boolean') flag = true;
   
   setContainers(this.rule_result_items);

};

/**
 * @method getOpenRuleResults
 *
 * @memberOf OpenAjax.a11y.formatters.TreeViewOfFilteredRuleResultsGroups
 *
 * @desc Returns an array of all the rule result items, including the 
 *       items in the children property of items with open property set to true
 *
 * @return  {Array} Array of rule result items optimized for a tree view
 */

OpenAjax.a11y.formatters.TreeViewOfFilteredRuleResultsGroups.prototype.getOpenRuleResults = function() {

  function getOpenItems(items) {
  
    if (typeof items !== 'object') return;
  
    var items_len = items.length;

    for (var i = 0; i < items_len; i++) {
    
      var item = items[i];
    
      list.push(item);
      
      if (item.open && item.children && item.children.length) { 
         getOpenItems(item.children);
      }
    }      
  }
      
  var list = [];

  var rr_items = this.rule_result_items;
  
  getOpenItems(rr_items);
  
  return list;
  
};

/**
 * @method sortedListOfRules
 *
 * @memberOf OpenAjax.a11y.formatters.TreeViewOfFilteredRuleResultsGroups
 *
 * @desc Sorts the list of rule result items based on item property values
 *
 * @param  {String}  sort_property - Name of property to sort, property names include: 'violations_count', 'warnings_count', 
 *                                   'failures_count', 'manual_checks_count', 'passed_count'
 *                                   
 * @param  {Number}  order         - If 1 sort ascending, else sort descending 
 *
 * @return  {Array} Array of objects optimized for display as a tree
 */

OpenAjax.a11y.formatters.TreeViewOfFilteredRuleResultsGroups.prototype.sortListOfRuleResults = function(sort_property, order) {

  function sortProperty(a, b) {
  
//    OpenAjax.a11y.logger.debug(" Group IDs: a=" + a.group_id  + " b=" + b.group_id);

    if (sort_property !== 'summary') {
      if (!a.has_results) return 1;
      if (!b.has_results) return -1;
    }  

    if (a[sort_property] > b[sort_property]) return  1 * order;
    if (a[sort_property] < b[sort_property]) return -1 * order;
    
    //tie breaker: name ascending is the second level sort
    
    if (sort_property != "violations_count") {
      if (a.violations_count > b.violations_count) return 1;
      if (a.violations_count < b.violations_count) return -1;
    }

    if (sort_property != "manual_checks_count") {
      if (a.manual_checks_count > b.manual_checks_count) return 1;
      if (a.manual_checks_count < b.manual_checks_count) return -1;
    }

    if (sort_property != "passed_count") {
      if (a.passed_count > b.passed_count) return 1;
      if (a.passed_count < b.passed_count) return -1;
    }
    
    if (sort_property != "hidden_count") {
      if (a.hidden_count > b.hidden_count) return 1;
      if (a.hidden_count < b.hidden_count) return -1;
    }
    
    return 0;  
  }
  
//  OpenAjax.a11y.logger.debug("  SORT ITEM: " + this.title + " SORT BY: " + sort_property + " ORDER: " + order);  
    
  for (var i = 0; i < this.rule_result_items.length; i++) {
  
    var rr_item = this.rule_result_items[i];
  
    var tv = rr_item.tree_view;

    if (tv) { 
      rr_item.children = tv.sortListOfRuleResults(sort_property, order);
    }
  
  }

  if (typeof sort_property !== 'string') sort_property = 'summary';
  if (typeof order !== 'number') order = 1;

  this.rule_result_items = this.rule_result_items.sort(sortProperty);

  return this.rule_result_items;

};


/* ---------------------------------------------------------------- */
/*        Tree View of Filtered Rule Results Group                  */
/* ---------------------------------------------------------------- */

/**
 * @constructor treeViewOfFilteredRuleResultsGroup
 *
 * @memberOf OpenAjax.a11y.formatters
 *
 * @desc Constructs a list of rule group object results that can be used by tree views 
 *       (i.e. XUL custom tree views)
 *
 * @param  {FilteredRuleResultsGroup}  filtered_rule_results_group  - Filtered rule 
 *                                                                    results group 
 *                                                                    object
 *
 * @param  {Number}  level  - level of this list in a tree structure 
 * 
 * @property {FilteredRuleResultsGroup}  filtered_rule_results_group - Filtered rule
 *                                                                     results group
 *                                                                     object reference 
 *
 * @property {Array}  rule_result_items  - List of rule result itmes with properties 
 *                                         optimized for tree view or list rendering   
 */

OpenAjax.a11y.formatters.TreeViewOfFilteredRuleResultsGroup = function(filtered_rule_results_group, level) {

  var cache_nls = OpenAjax.a11y.cache_nls.getCacheNLS();
  var boolean_values = cache_nls.boolean_values;

  if (typeof level !== 'number') level = 0;
  
  // Initialize object properties
  this.filtered_rule_results_group = filtered_rule_results_group;  
  this.rule_result_items = [];

//  OpenAjax.a11y.logger.debug("  create tree view: " + filtered_rule_results_group + " " + filtered_rule_results_group.filtered_rule_results);  

  var filtered_rule_results     = filtered_rule_results_group.filtered_rule_results;  
  var filtered_rule_results_len = filtered_rule_results.length;

  for (var i = 0; i < filtered_rule_results_len; i++) {
  
    var frr =  filtered_rule_results[i];
    var rs  = frr.getNodeResultSummary();
    var has_results = rs.hasResults();

    var failures_prop = "zero";
    
    if (rs.violations > 0)  failures_prop  = "violation";
    if (rs.warnings   > 0)  failures_prop  = "warning";
    
    var rr_item = {
      // properties for tree view
      container : false,
      open      : false,
      level     : level,
      type      : 'FILTERED_RULE_RESULT',
      
      // Filtered rule result properties
      
      filtered_rule_result : frr,
      
      children      : [],

      group_id      : frr.group_id,
      
      has_results   : has_results,
      
      required       : frr.isRuleRequired(),      
      required_label : frr.getRuleRequiredYesNo(),
      required_prop  : frr.getRuleRequiredYesNo().toLowerCase(),
      

      summary       : frr.getRuleSummary(),
      summary_prop  : has_results ? "" : "not_applicable",
      
      definition    : frr.getRuleDefinition(),
      messages      : frr.getResultMessages(),
      message       : frr.getResultMessage(),

      wcag20_level       : frr.getWCAG20LevelConstant(),
      wcag20_level_label : frr.getWCAG20Level(),
      wcag20_level_prop  : frr.getWCAG20Level().toLowerCase(),
      
      percent_passed       : rs.percent_passed,
      percent_passed_label : has_results ? rs.percent_passed.toString() : '-',
      percent_passed_prop  : has_results ? 'passed' : 'zero',

      passed_count         : rs.passed,
      passed_label         : has_results ? rs.passed.toString() : '-',
      passed_prop          : has_results ? 'passed' : 'zero',

      violations_count     : rs.violations,
      violations_label     : has_results ? rs.violations.toString() : '-',
      violations_prop      : (rs.violations >= 1) ? 'violation' : 'zero',
      
      warnings_count       : rs.warnings,
      warnings_label       : has_results ? rs.warnings.toString() : '-',
      warnings_prop        : (rs.warnings >= 1) ? 'warning' : 'zero',
      
      failures_count       : rs.failures,
      failures_label       : has_results ? rs.failures.toString() : '-',
      failures_prop        : (rs.failures >= 1) ? failures_prop : 'zero',
            
      manual_checks_count  : rs.manual_checks,
      manual_checks_label  : has_results ? rs.manual_checks.toString() : '-',
      manual_checks_prop   : (rs.manual_checks >= 1) ? 'manual_check' : 'zero',      
      
      hidden_count         : rs.hidden,
      hidden_label         : has_results ? rs.hidden.toString() : '-',
      hidden_prop          : (rs.hidden >= 1) ? 'hidden' : 'zero'
    
    };

    this.rule_result_items.push(rr_item);
   
//    OpenAjax.a11y.logger.debug("  PERCENT PASS LABEL: " + rrg_item.percent_passed_label);
  }

};

/**
 * @method getOpenRuleResults
 *
 * @memberOf OpenAjax.a11y.formatters.TreeViewOfFilteredRuleResultsGroup
 *
 * @desc Returns an array of all the rule result items, including the 
 *       items in the children property of items with open property set to true
 *
 * @return  {Array} Array of rule result items optimized for a tree view
 */

OpenAjax.a11y.formatters.TreeViewOfFilteredRuleResultsGroup.prototype.getOpenRuleResults = function() {

  return this.rule_result_items;
  
};

/**
 * @method sortedListOfRules
 *
 * @memberOf OpenAjax.a11y.formatters.TreeViewOfFilteredRuleResultsGroup
 *
 * @desc Sorts the list of rule result items based on item property values
 *
 * @param  {String}  sort_property - Name of property to sort, property names include: 'violations_count', 'warnings_count', 
 *                                   'failures_count', 'manual_checks_count', 'passed_count'
 *
 * @param  {Number}  order         - If 1 sort ascending, else sort descending 
 *
 * @return  {Array} Array of objects optimized for display as a tree
 */

OpenAjax.a11y.formatters.TreeViewOfFilteredRuleResultsGroup.prototype.sortListOfRuleResults = function(sort_property, order) {

  function sortProperty(a, b) {
  
//     OpenAjax.a11y.logger.debug("  COMPARE RULES A:(" + a.summary + ") B:(" + b.summary + ")");  

    if (sort_property !== 'summary' || sort_property !== 'percent_passed') {
      if (!a.has_results) return 1;
      if (!b.has_results) return -1;
    }  

    if (a[sort_property] > b[sort_property]) return  1 * order;
    if (a[sort_property] < b[sort_property]) return -1 * order;
    
    //tie breaker: name ascending is the second level sort
    
    if (sort_property != "wcag20_level") {
      if (a.wcag20_level < b.wcag20_level) return 1;
      if (a.wcag20_level > b.wcag20_level) return -1;
    }
    
    if (sort_property != "required") {
      if (!a.required &&  b.required) return 1;
      if ( a.required && !b.required) return -1;
    }

    if (sort_property != "violations_count") {
      if (a.violations_count < b.violations_count) return 1;
      if (a.violations_count > b.violations_count) return -1;
    }

    if (sort_property != "manual_checks_count") {
      if (a.manual_checks_count < b.manual_checks_count) return 1;
      if (a.manual_checks_count > b.manual_checks_count) return -1;
    }

    if (sort_property != "passed_count") {
      if (a.passed_count > b.passed_count) return 1;
      if (a.passed_count < b.passed_count) return -1;
    }
    
    if (sort_property != "hidden_count") {
      if (a.hidden_count < b.hidden_count) return 1;
      if (a.hidden_count > b.hidden_count) return -1;
    }
    
    return 0;  
  }

  if (typeof sort_property !== 'string') sort_property = 'wcag20_level';
  if (typeof order !== 'number') order = -1;

//  OpenAjax.a11y.logger.debug("  SORT ITEM: " + this.title + " SORT BY: " + sort_property + " ORDER: " + order);  

  this.rule_result_items = this.rule_result_items.sort(sortProperty);

  return this.rule_result_items.sort(sortProperty);

};

/* ---------------------------------------------------------------- */
/*                           FilteredRuleResult                     */
/* ---------------------------------------------------------------- */

/**
 * @constructor TreeViewOfFilteredRuleResult
 *
 * @memberOf OpenAjax.a11y.formatters
 *
 * @desc Creates a list of node results formatted for display in a tree view
 *          
 * @param  {FilteredRuleResult}  filtered_rule_result  - Filtered rule result 
 *                                                       object 
 *
 * @property {FilteredRuleResult}  filtered_rule_result  - Filtered rule result
 *                                                         object reference
 *
 * @property {Array}  node_result_items - List of node result items optimized
 *                                        for tree or list view rendering
 */

 OpenAjax.a11y.formatters.TreeViewOfFilteredRuleResult = function(filtered_rule_result) {

  function addFilteredElementCount(nr_items, count) {

    if (count === 0) return;

    var str;

    if (count === 1) str = count + " " + cache_nls.filteredItem;
    else str = count + " "  + cache_nls.filteredItems;

    var row = { level : 0,
                container : false,
                open : false,
                
                node_result : null,
                position : -1,
                
                message : str,
                message_prop : "message"
        };

    nr_items.push(row);
  }

  function addProperty(nr_item, label, value) {
  
    var prop_item = { level     : 1,
                      container : false,
                      open      : false,

                      node_result : nr_item.node_result,
                      position    : nr_item.position,

                      property_name      : label,  
                      property_name_prop : "",
                      value              : "",
                      value_prop         : "" 
                   };
 
    if (value !== null) {
      // if value is a string test to see if it is empty
      if (typeof value == "string") {
        if (value.length) {
          prop_item.value = value;
        } 
        else {
          prop_item.value      = cache_nls.empty;
          prop_item.value_prop = 'empty';
        }
      }
      else {
        prop_item.value = value.toString();
      }  
    }  
    else {
      prop_item.value = cache_nls.undefined;
      prop_item.value_prop = 'empty';
    }
    
    nr_item.properties.push(prop_item);    
        
  }

  var cache_nls = OpenAjax.a11y.cache_nls.getCacheNLS();
  var boolean_values = cache_nls.boolean_values;

  // Initialize properties
  this.filtered_rule_result = filtered_rule_result;  
  this.node_result_items = [];
  
  var node_results     = filtered_rule_result.filtered_node_results;
  var node_results_len = node_results.length;

  for (var i = 0; i < node_results_len; i++) {
  
    var nr = node_results[i];
    
    var properties = nr.getTargetResourceProperties();
    
    var nr_item = { level        : 0,
                    container    : false,
                    open         : false,

                    properties   : [],
                    node_result  : nr,
                    
                    severity     : nr.getResultValue(),
                    
                    element      : nr.getCacheItem().toString(), 
                    element_prop : "element",
               
                    order       : nr.getOrdinalPosition(),
                    order_prop  : "order",

                    result       : nr.getResultValueStringsNLS().abbrev,
                    result_prop  : nr.getResultValueStringsNLS().style,

                    result_message      : nr.getResultMessage(), 
                    result_message_prop : "result_message_prop"


                 };

/*
    var message_str = cache_nls.message;    
    // Capitalize
    message_str = message_str.charAt(0).toUpperCase() + message_str.slice(1);
    addProperty(nr_item, message_str, nr.getResultMessage());
        
    if (!nr.cache_item.is_page_element) {
      for (var j = 0; j < properties.length; j++) {
        addProperty(nr_item, properties[j].label, properties[j].value);
      } // end for  
    } 
*/

   this.node_result_items.push(nr_item);
  
  }
  
  addFilteredElementCount(this.node_result_items, filtered_rule_result.node_results_filtered_out);
  
};


/**
 * @method setOpenState
 *
 * @memberOf OpenAjax.a11y.formatters.TreeViewOfFilteredRuleResult
 *
 * @desc Sets open state on all container items in the list
 *       Used for expanding or collapsing all leafs in a tree
 *
 * @param  {Boolean} flag  -  If true all container elements will be set 
 *                            to open, otherwise containers element set 
 *                            to close  
 */

OpenAjax.a11y.formatters.TreeViewOfFilteredRuleResult.prototype.setOpenState = function (flag) {

   function setContainers(list) {
   
     for (var i = 0; i < list.length; i++) {
   
        var item = list[i];
        
        if (item.container) {
          item.open = flag;
          
          if (item.properties && item.properties.length) setContainers(item.properties);
        }     
     }
   }
   
   if (typeof flag !== 'boolean') flag = true;
   
   setContainers(this.node_result_items);

};


/**
 * @method getOpenNodeResults
 *
 * @memberOf OpenAjax.a11y.formatters.TreeViewOfFilteredRuleResult
 *
 * @desc Returns an array of all the node result items, including the 
 *       items in the children property of items with open property set to true
 *
 * @return  {Array} Array of node result items optimized for a tree view
 */

OpenAjax.a11y.formatters.TreeViewOfFilteredRuleResult.prototype.getOpenNodeResults = function() {

  function getOpenItems(items) {
  
    if (typeof items !== 'object') return;
  
    var items_len = items.length;

    for (var i = 0; i < items_len; i++) {
    
      var item = items[i];
    
      list.push(item);
      
      if (item.open && item.properties && item.properties.length) { 
         getOpenItems(item.properties);
      }
    }      
  }
      
  var list = [];
  
  getOpenItems(this.node_result_items);
  
  return list;
  
};

/**
 * @method sortListOfNodeResults
 *
 * @memberOf OpenAjax.a11y.formatters.TreeViewOfFilteredRuleResult
 *
 * @desc Sorts the list of node result items based on item property values
 *
 * @param  {String}  sort_property - Name of property to sort, property names 
 *                                   include: 'result', 'element', 'position'
 *
 * @param  {Number}  order         - If 1 sort ascending, else sort descending 
 *
 * @return  {Array} Array of objects optimized for display as a tree
 */

OpenAjax.a11y.formatters.TreeViewOfFilteredRuleResult.prototype.sortListOfNodeResults = function(sort_property, order) {

  function sortProperty(a, b) {
  
    if (typeof a.message === 'string') return 1;
    if (typeof b.message === 'string') return -1;
    
    if (a[sort_property] > b[sort_property]) return  -1 * order;
    if (a[sort_property] < b[sort_property]) return 1 * order;
    
    //tie breaker: name ascending is the second level sort

    if (sort_property != "severity") {
      if (a.severity > b.severity) return 1;
      if (a.severity < b.severity) return -1;
    }
    
    if (sort_property != "order") {
      if (a.order > b.order) return 1;
      if (a.order < b.order) return -1;
    }
    
    if (sort_property != "element") {
      if (a.element > b.element) return 1;
      if (a.element < b.element) return -1;
    }

    if (sort_property != "result_message") {
      if (a.result_message > b.result_message) return 1;
      if (a.result_message < b.result_message) return -1;
    }
    

    return 0;  
  }

  return this.node_result_items.sort(sortProperty);

};


/**
 * @constructor TreeViewOfFilteredCacheItemResults
 *
 * @memberOf OpenAjax.a11y.formatters
 *
 * @desc Constructs a list of cache item results that can be used by tree views 
 *       (i.e. XUL custom tree views)
 *
 * @param  {FilteredCacheItemResults}  filtered_cache_item_results  - Filtered cache 
 *                                                                    item result object
 *                                                                    for tree view 
 *                                                                    presentation
 * 
 * @property {FilteredCacheItemResults}   filtered_cache_item_results - Filtered cache
 *                                                                      item result object
 *                                                                      reference
 *
 * @property {Array}  cache_item_results - An array of cache item result items for use
 *                                         be tree or list view rendering
 */

OpenAjax.a11y.formatters.TreeViewOfFilteredCacheItemResults = function(filtered_cache_item_results) {

  function addCacheItemResults(cache_item_results, level) {
  
    var cache_item_results_len = cache_item_results.length;
    
    var list = [];

    for (var i = 0; i < cache_item_results_len; i++) {

      var cir =  cache_item_results[i];
      
      var rs = cir.getNodeResultSummary();

//      OpenAjax.a11y.logger.debug("  Cache Item: " + cir.cache_item.toString());

      var failures_prop  = "zero";    
      if (rs.warnings   > 0)  failures_prop  = "warning";
      if (rs.violations > 0)  failures_prop  = "violation";

      var children = [];
      var position = position_count;
      position_count++;
      if (cir.is_tree) children = addCacheItemResults(cir.children, (level+1));
      
      var c_item = {
        // properties for tree view
        container : cir.is_tree,
        open      : cir.is_tree,
        level     : level,
        type      : 'CACHE_ITEM_RESULT',
      
        // Filtered rule result properties
    
        position            : cir.getOrdinalPosition(),
        cache_item_result   : cir,
        node_results        : cir.getNodeResults(),
    
        children     : children,
            
        element      : cir.getCacheItem().toString(),
        element_prop : "",
      
        passed_count : rs.passed,
        passed_label : rs.passed.toString(),
        passed_prop  : (rs.passed > 0) ? 'passed' : 'zero',

        violations_count : rs.violations,
        violations_label : rs.violations.toString(),
        violations_prop  : (rs.violations > 0) ? 'violation' : 'zero',
      
        warnings_count : rs.warnings,
        warnings_label : rs.warnings.toString(),
        warnings_prop  : (rs.warnings > 0) ? 'warning' : 'zero',

        failures_count : rs.failures,
        failures_label : rs.failures.toString(),
        failures_prop  : (rs.failures > 0) ? failures_prop : 'zero', 

        manual_checks_count : rs.manual_checks,
        manual_checks_label : rs.manual_checks.toString(),
        manual_checks_prop  : (rs.manual_checks > 0) ? 'manual_check' :' zero',      
      
        hidden_count : rs.hidden,
        hidden_label : rs.hidden.toString(),
        hidden_prop  : (rs.hidden > 0) ? 'hidden' : 'zero'
    
      };
      
      list.push(c_item);
  
    }
    
    return list;
    
  } // end addCacheItemResults   
  
  var position_count = 1;
  
  this.filtered_cache_item_results = filtered_cache_item_results;

//  OpenAjax.a11y.logger.debug("CACHE ITEM LIST: " + this.element_type + " (" + this.cache_item_results + ")");

  this.cache_item_list = addCacheItemResults(filtered_cache_item_results.cache_item_results, 0);
    
};

/**
 * @method setOpenState
 *
 * @memberOf OpenAjax.a11y.formatters.TreeViewOfFilteredCacheItemResults
 *
 * @desc Sets open state on all container items in the list
 *       Used for expanding or collapsing all leafs in a tree
 *
 * @param  {Boolean} flag  -  If true all container elements will be set 
 *                            to open, otherwise containers element set 
 *                            to close  
 */

OpenAjax.a11y.formatters.TreeViewOfFilteredCacheItemResults.prototype.setOpenState = function (flag) {

   function setContainers(list) {
   
     for (var i = 0; i < list.length; i++) {
   
        var item = list[i];
        
        if (item.container) {
          item.open = flag;
          
          if (item.children && item.children.length) setContainers(item.children);
        }     
     }
   }
   
   if (typeof flag !== 'boolean') flag = true;
   
   setContainers(this.cache_item_list);

};

/**
 * @method getOpenCacheItemResults
 *
 * @memberOf OpenAjax.a11y.formatters.TreeViewOfFilteredCacheItemResults
 *
 * @desc Creates an array of cache item result objects with properties optimized for customized tree view display, that can be sorted
 *
 * @param  {Boolean} all_flag  -  If true all elements of the list including children are returned without levels 
 *
 * @return  {Array} Array of objects optimized for display as a XUL custom tree
 */

OpenAjax.a11y.formatters.TreeViewOfFilteredCacheItemResults.prototype.getOpenCacheItemResults = function(all_flag) {

  function getOpenItems(items) {
  
    if (typeof items !== 'object') return;
  
    var items_len = items.length;

    for (var i = 0; i < items_len; i++) {
    
      var item = items[i];
    
      if (all_flag) {
        item.container = false;
        item.open      = true;
        item.level     = 0;
        
        list.push(item);      
        
        if (item.children && item.children.length) { 
          getOpenItems(item.children);
        }
      }
      else {
      
        list.push(item);      
        
        if (item.open && item.children && item.children.length) { 
          getOpenItems(item.children);
        }
      }  
    }      
  }
      
  if (typeof all_flag !== 'boolean') all_flag = false;     
      
  var list = [];

  var ci_list = this.cache_item_list;
  
  getOpenItems(ci_list);
  
  this.open_cache_item_list = list;
  
  return list;

};

/**
 * @method sortListOfCacheItemResults
 *
 * @memberOf OpenAjax.a11y.formatters.TreeViewOfFilteredCacheItemResults
 *
 * @desc Sorts an array of cache item results based on one of the properties
 *       of an item
 *
 * @param  {String}  sort_property - Name of property to sort 
 *                                   (i.e. 'element', 'violation' ..)
 * 
 * @param  {Number}  order         - If 1 sort ascending, else sort descending 
 *
 * @return  {Array} Sorted list of cache items 
 */

OpenAjax.a11y.formatters.TreeViewOfFilteredCacheItemResults.prototype.sortListOfCacheItemResults = function(sort_property, order) {

  function sortProperty(a, b) {
  
//    OpenAjax.a11y.logger.debug(" Group IDs: a=" + a.position  + " b=" + b.position);

    if (a[sort_property] > b[sort_property]) return  1 * order;
    if (a[sort_property] < b[sort_property]) return -1 * order;
    
    //tie breaker: name ascending is the second level sort
    
    if (sort_property != "violations_count") {
      if (a.violations_count > b.violations_count) return 1;
      if (a.violations_count < b.violations_count) return -1;
    }

    if (sort_property != "manual_checks_count") {
      if (a.manual_checks_count > b.manual_checks_count) return 1;
      if (a.manual_checks_count < b.manual_checks_count) return -1;
    }

    if (sort_property != "passed_count") {
      if (a.passed_count > b.passed_count) return 1;
      if (a.passed_count < b.passed_count) return -1;
    }
    
    if (sort_property != "hidden_count") {
      if (a.hidden_count > b.hidden_count) return 1;
      if (a.hidden_count < b.hidden_count) return -1;
    }

    if (sort_property != "position") {
      if (a.position > b.position) return 1;
      if (a.position < b.position) return -1;
    }
    

    return 0;  
  }
  
  if (typeof sort_property !== 'string') sort_property = 'position';
  if (typeof order !== 'number') order = 1;

//  OpenAjax.a11y.logger.debug("CACHE ITEM RESULTS SORT: prop=" + sort_property  + " order=" + order);

  var open_cache_item_list = this.getOpenCacheItemResults(true);

  open_cache_item_list.sort(sortProperty);

  return open_cache_item_list;

};

/**
 * @constructor TreeViewOfCacheItemResult
 *
 * @memberOf OpenAjax.a11y.formatters
 *
 * @desc Constructs a list of node results for a cache item result 
 *       that can be used by tree views (i.e. XUL custom tree views)
 *
 * @param  {CacheItemResult}  cache_item_result  - Filtered cache item result 
 *                                                 object for tree view presentation
 * 
 * @property {CacheItemResult}  cache_item_result  - Cache item result object
 *                                                   reference
 *
 * @property {Array}  node_result_items  -  An array of cache item result items
 *                                          for use be tree or list view rendering
 */

OpenAjax.a11y.formatters.TreeViewOfCacheItemResult = function(cache_item_result) {

  function addFilteredElementCount(count) {

    if (typeof count !== 'number' || count === 0) return;

    var str;

    if (count === 1) str = count + " " + cache_nls.filteredRule;
    else str = count + " "  + cache_nls.filteredRules;

    var row = { level : 0,
                container : false,
                open : false,
                
                node_result : null,
                position : -1,
                
                message : str,
                message_prop : "message"
        };

    nr_items.push(row);
  }

  function addProperty(nr_item, label, value) {
  
    var prop_item = { level     : 1,
                      container : false,
                      open      : false,

                      node_result : nr_item.node_result,
                      rule_number : rule_number,
                      position    : nr_item.position,

                      property_name      : label,  
                      property_name_prop : "",
                      value              : "",
                      value_prop         : "" 
                   };
 
    if (value !== null) {
      // if value is a string test to see if it is empty
      if (typeof value == "string") {
        if (value.length) {
          prop_item.value = value;
        } 
        else {
          prop_item.value      = cache_nls.empty;
          prop_item.value_prop = 'empty';
        }
      }
      else {
        prop_item.value = value.toString();
      }  
    }  
    else {
      prop_item.value = cache_nls.undefined;
      prop_item.value_prop = 'empty';
    }
    
    nr_item.properties.push(prop_item);    
        
  }

  var cache_nls = OpenAjax.a11y.cache_nls.getCacheNLS();

  var boolean_values = cache_nls.boolean_values;

  var node_results     = cache_item_result.filtered_node_results;
  var node_results_len = node_results.length;

  var nr_items = []; 

  for (var i = 0; i < node_results_len; i++) {
  
    var n_result =  node_results[i];
    
    var nr_item = { level       : 0,
                    container   : false,
                    open        : false,

                    node_result : n_result,

                    required_label : n_result.getRuleRequiredYesNo(),
                    required       : n_result.isRuleRequired(),

                    level_label  : n_result.getWCAG20Level(),
                    level_const  : n_result.getWCAG20LevelConstant(),
               
                    result_value : n_result.getResultValueConstant(),
                    result_label : n_result.getResultValue().label,
                    result_prop  : n_result.getResultValue().style,
               
                    rule_label   : n_result.getRuleSummary(), 
                    rule_prop    : "rule"
                 };
                        
   nr_items.push(nr_item);
  
  }
  
  if (typeof this.number_of_node_results_filtered === 'number') addFilteredElementCount(this.number_of_node_results_filtered);

  
  this.node_result_items = nr_items;

};

/**
 * @method setOpenState
 *
 * @memberOf OpenAjax.a11y.formatters.TreeViewOfCacheItemResult
 *
 * @desc Sets open state on all container items in the list
 *       Used for expanding or collapsing all leafs in a tree
 *
 * @param  {Boolean} flag  -  If true all container elements will be set 
 *                            to open, otherwise containers element set 
 *                            to close  
 */

OpenAjax.a11y.formatters.TreeViewOfCacheItemResult.prototype.setOpenState = function (flag) {

   function setContainers(list) {
   
     for (var i = 0; i < list.length; i++) {
   
        var item = list[i];
        
        if (item.container) {
          item.open = flag;
          
          if (item.properties && item.properties.length) setContainers(item.properties);
        }     
     }
   }
   
   if (typeof flag !== 'boolean') flag = true;
   
   setContainers(this.node_result_items);

};


/**
 * @method getOpenNodeResults
 *
 * @memberOf OpenAjax.a11y.formatters.TreeViewOfCacheItemResult
 *
 * @desc Returns an array of all the node result items, including the 
 *       items in the children property of items with open property set to true
 *
 * @return  {Array} Array of node result items optimized for a tree view
*/

OpenAjax.a11y.formatters.TreeViewOfCacheItemResult.prototype.getOpenNodeResults = function() {

  function getOpenItems(items) {
  
    if (typeof items !== 'object') return;
  
    var items_len = items.length;

    for (var i = 0; i < items_len; i++) {
    
      var item = items[i];
    
      list.push(item);
      
      if (item.open && item.properties && item.properties.length) { 
         getOpenItems(item.properties);
      }
    }      
  }
      
  var list = [];

  var nr_items = this.node_result_items;
  
  getOpenItems(nr_items);
  
  return list;
  
};

/**
 * @method sortListOfNodeResults
 *
 * @memberOf OpenAjax.a11y.formatters.TreeViewOfCacheItemResult
 *
 * @desc Sorts the list of node result items based on item property values
 *
 * @param  {String}  sort_property - Name of property to sort, property names 
 *                                   include: 'result', 'element', 'position'
 *
 * @param  {Number}  order         - If 1 sort ascending, else sort descending 
 *
 * @return  {Array} Array of objects optimized for display as a tree
 */

OpenAjax.a11y.formatters.TreeViewOfCacheItemResult.prototype.sortListOfNodeResults = function(sort_property, order) {

  function sortProperty(a, b) {
  
    if (a[sort_property] > b[sort_property]) return  -1 * order;
    if (a[sort_property] < b[sort_property]) return 1 * order;
    
    //tie breaker: name ascending is the second level sort
    
    if (sort_property != "result") {
      if (a.result > b.result) return -1;
      if (a.result < b.result) return 1;
    }

    if (sort_property != "level_const") {
      if (a.level_const > b.level_const) return -1;
      if (a.level_const < b.level_const) return 1;
    }

    
    return 0;  
  }

  return this.node_result_items.sort(sortProperty);

};



/*
 * Copyright 2011-2013 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

OpenAjax.a11y.nls = OpenAjax.a11y.nls || {};

/* ---------------------------------------------------------------- */
/*                            DOMCacheNLS                          */
/* ---------------------------------------------------------------- */

/**
 * @constructor Cache
 *
 * @memberOf OpenAjax.a11y.nls
 *
 * @desc Constructs a DOMCache Object 
 *          
 * @property {String}  nls       - NLS cache items for properties
 */
 
OpenAjax.a11y.nls.Cache = function() {

  this.nls = {};

};

/**
 * @method addCacheNLSFromJSON()
 *
 * @memberOf OpenAjax.a11y.nls.Cache
 *
 * @desc Constructs a DOMCache Object 
 *
 * @param  {locale}  locale         - Language code 
 * @param  {Object}  cache_nls_data - NLS cache items for properties
 */
 
OpenAjax.a11y.nls.Cache.prototype.addCacheNLSFromJSON = function(locale, cache_nls_data) {

  this.nls[locale] = cache_nls_data;

};

/**
 * @method getCacheNLS
 *
 * @memberOf OpenAjax.a11y.nls.Cache
 *
 * @desc Returns the current cache nls object 
 *
 */
 
OpenAjax.a11y.nls.Cache.prototype.getCacheNLS = function() {

  return this.nls[OpenAjax.a11y.locale];

};

/**
 * @method getResultValueNLS
 *
 * @memberOf OpenAjax.a11y.nls.Cache
 *
 * @desc Gets severity label, abbreviation, description and style 
 *
 * @param  {Number}  result_value  -  The constant representing the result of the 
 *                                    evaluation (i.e. violation, warning, passed...)
 *
 * @return {Object} Returns a object with the following properties: <br/>
 *                  'label'       : String representing the severity <br/>
 *                  'abbrev'      : Abbreviation string of the label<br/>
 *                  'description' : String describing the severity<br/>
 *                  'style'       : String that can used for styling the label<br/> 
 */
 
OpenAjax.a11y.nls.Cache.prototype.getResultValueNLS = function(result_value) {

  var obj = this.nls[OpenAjax.a11y.locale].rule_types[result_value];
  
  obj.toString = function () { return this.label; };

  return obj;
};

/**
 * @method getRuleCategory
 *
 * @memberOf OpenAjax.a11y.nls.Cache
 *
 * @desc Get localized string for a rule category
 *
 * @param  {Number}  id  -  The constant representing the rule category
 *
 * @return {Object} Returns the NLS object for a rule category, empty object if not found
 *                  Properties of the object include: "title", "url" and "desc"
 */
 
OpenAjax.a11y.nls.Cache.prototype.getRuleCategory = function(id) {

  var rc = this.nls[OpenAjax.a11y.locale].rule_categories;
  var rc_num = rc.length;
  
  for (var i = 0; i < rc_num; i++) {
    if (rc[i].id === id) return rc[i];
  }
  
  var ro = {
    title : "Rule category with the the following ID not found: " + id,
    url   : "",
    desc  : ""
  };
  
  return ro;

};


/**
 * @method getLabelAndValueNLS
 *
 * @memberOf OpenAjax.a11y.nls.Cache
 *
 * @desc Returns the label, human readable value and description of a cache property
 * 
 * @param  {String}           property  - The object property
 * @param  {String | Number}  value     - Current value of a property
 *
 * @return {Object} Returns object with three properties 'label', 'value' and 'description'
 */
 
OpenAjax.a11y.nls.Cache.prototype.getLabelAndValueNLS = function (property, value) {
  
    var info = {};  // return object 
    
    info.label       = property;
    info.value       = value;
    info.description = "";
    
    var nls_cache = this.nls[OpenAjax.a11y.locale];
    
    if (nls_cache) {

      var cp = nls_cache.resource_properties[property];
      
      // if null return default
      if (!cp) return info;

      if (cp.label)       info.label       = cp.label;
      if (cp.description) info.description = cp.description;        
      
      switch(typeof value) {
      
      case 'boolean': 
      
        if (value)
          info.value = nls_cache.boolean_values.true_value;
        else
          info.value = nls_cache.boolean_values.false_value;
        break;
        
      case 'number':
      
        if (cp.values) 
          info.value = cp.values[value].toString();
        else
          info.value = String(value);
          
        break; 

      default:
         break;
      }      
    } 
    
    return info;
  
};

/**
 * @method getLabelNLS
 *
 * @memberOf OpenAjax.a11y.nls.Cache
 *
 * @desc Returns the label and description of a cache property
 *
 * @param  {String}  property  - The object property
 * 
 * @return {Object} Returns object with two properties 'label' and 'description'
 */
 
OpenAjax.a11y.nls.Cache.prototype.getLabelNLS = function (property) {
  
    var info = {};  // return object 
    
    info.label       = property;
    info.description = "";
    
    var nls_cache = this.nls[OpenAjax.a11y.locale];
    
    if (nls_cache) {
     
      var cp = nls_cache.resource_properties[property];
      
      // if null return default
      if (!cp) return info;

      if (cp.label)       info.label       = cp.label;
      if (cp.description) info.description = cp.description;        
              
    } 
    
    return info;
  
};

/**
 * @method getValueNLS
 *
 * @memberOf OpenAjax.a11y.nls.Cache
 *
 * @desc Returns the value of a cache property
 *
 * @param  {String}           property  - The object property
 * @param  {String | Number}  value     - Current value of a property
 * 
 * @return {String} Returns string with the localized value of a property
 */
 
OpenAjax.a11y.nls.Cache.prototype.getValueNLS = function (property, value) {
  
    var str = "";  // return object 
       
    var nls_cache = this.nls[OpenAjax.a11y.locale];
    
    if (nls_cache) {

      var cp = nls_cache.resource_properties[property];
      
      // if null return default
      if (!cp) return value;

      switch(typeof value) {
      
      case 'boolean': 
      
        if (value)
          str = nls_cache.boolean_values.true_value;
        else
          str = nls_cache.boolean_values.false_value;
        break;
        
      case 'number':
      
        if (cp.values) 
          str = cp.values[value].toString();
        else
          str = String(value);
          
        break; 

      default:
         break;
      }      
              
    } 
    
    return str;
  
};

/**
 * @method getYesNoNLS
 *
 * @memberOf OpenAjax.a11y.nls.Cache
 *
 * @desc Get a localized "Yes" or "No" string 
 *
 * @param {Boolean}  value  - A boolean value to get  string
 * 
 * @return {String} Returns 'Yes" if true, otherwise 'No' 
 */
 
OpenAjax.a11y.nls.Cache.prototype.getYesNoNLS = function (value) {
  
  var nls_cache = this.nls[OpenAjax.a11y.locale];
  
  if (typeof value !== 'boolean') return nls_cache.not_boolean_value;  
    
  if (value) return nls_cache.yes_no_values.yes_value;
    
  return nls_cache.yes_no_values.no_value;
  
};


/**
 * @method getNLSMissingLabelMessage
 *
 * @memberOf OpenAjax.a11y.nls.Cache
 *
 * @desc Returns the missing form control label message and style
 * 
 * @return {String} Returns an object with a 'label' and 'style' property
 */
 
OpenAjax.a11y.nls.Cache.prototype.getNLSMissingLabelMessage = function () {
  
    var label_style;  // return object    
       
    var nls_cache = this.nls[OpenAjax.a11y.locale];    
    
    if (nls_cache) {
     
      label_style = nls_cache.missing_label;
      
      // if null return default
      if (!label_style) return "";
             
    } 
    
    return label_style;
  
};

/**
 * @method getNLSEmptyAltTextMessage
 *
 * @memberOf OpenAjax.a11y.nls.Cache
 *
 * @desc Returns the empty alt text message message and style
 * 
 * @return {String} Returns an object with a 'label' and 'style' property
 */
 
OpenAjax.a11y.nls.Cache.prototype.getNLSEmptyAltTextMessage = function () {
  
    var label_style;  // return object    
       
    var nls_cache = this.nls[OpenAjax.a11y.locale];    
    
    if (nls_cache) {
     
      label_style = nls_cache.empty_alt_text;
      
      // if null return default
      if (!label_style) return "";
             
    } 
    
    return label_style;
  
};

/**
 * @method getNLSMissingAltMessage
 *
 * @memberOf OpenAjax.a11y.nls.Cache
 *
 * @desc Returns an NLS localized 'missing alt attribute' message
 * 
 * @return {String} Returns an object with a 'label' and 'style' property
 */
 
OpenAjax.a11y.nls.Cache.prototype.getNLSMissingAltMessage = function () {
  
    var label_style;  // return object    
       
    var nls_cache = this.nls[OpenAjax.a11y.locale];    
    
    if (nls_cache) {
     
      label_style = nls_cache.missing_alt;
      
      // if null return default
      if (!label_style) return "";
             
    } 
    
    return label_style;
  
};


/**
 * @method addPropertyIfDefined
 *
 * @memberOf OpenAjax.a11y.nls.Cache
 *
 * @desc Adds an item to a list of properties 
 */

OpenAjax.a11y.nls.Cache.prototype.addPropertyIfDefined = function (list, item, property) {

  if ((typeof item[property] !== 'undefined')) {
    list.push(this.getLabelAndValueNLS(property, item[property]));
  } // endif
  
};

/**
 * @method addPropertyIfUnefined
 *
 * @memberOf OpenAjax.a11y.nls.Cache
 *
 * @desc Adds an item to a list of properties if it is not defined
 */

OpenAjax.a11y.nls.Cache.prototype.addPropertyIfUndefined = function (list, item, property) {

//  OpenAjax.a11y.logger.debug("Undefined '" + item + "': " + item[property]);

  if ((typeof item[property] === 'undefined') || 
      (item[property] === null) || 
      (item[property] === "")) {
    list.push(this.getLabelAndValueNLS(property, 'undefined'));
  } // endif
  
};
/**
 * @method addInvalidAttribute
 *
 * @memberOf OpenAjax.a11y.nls.Cache
 *
 * @desc Identifies an invalid attribute
 */

OpenAjax.a11y.nls.Cache.prototype.addInvalidAttribute = function (list, attribute) {

  var nls_cache = this.nls[OpenAjax.a11y.locale];
  var o = {};
  
  o.label = nls_cache.invalid_attribute.label;
  o.value = attribute;
  o.style = nls_cache.invalid_attribute.style;

  list.push(o);
  
};

/**
 * @method addInvalidValue
 *
 * @memberOf OpenAjax.a11y.nls.Cache
 *
 * @desc Identifies an invalid attribute value
 */

OpenAjax.a11y.nls.Cache.prototype.addInvalidValue = function (list, attribute, value) {

  var nls_cache = this.nls[OpenAjax.a11y.locale];
  var o = {};
  
  o.label = attribute;
  o.value = value + " " + nls.invalid_value.value;
  o.style = nls.invalid_value.style;

  list.push(o);
    
};
/*
 * Copyright 2011-2013 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

OpenAjax.a11y.nls = OpenAjax.a11y.nls || {};


/* ---------------------------------------------------------------- */
/*                       WCAG20                                     */
/* ---------------------------------------------------------------- */

/**
 * @constructor WCAG20
 *
 * @memberOf OpenAjax.a11y.nls
 *
 * @desc WCAG 2.0 information with properties with localized NLS values 
 *
 * @property  {Array}   nls - Associative array of WCAG 2.0 information 
 */

OpenAjax.a11y.nls.WCAG20 = function() {

  this.nls = {};
  
};

/**
 * @method addNLS
 *
 * @memberOf OpenAjax.a11y.nls.WCAG20
 *
 * @desc Adds a localized version of WCAG 2.0 requirements to the cache 
 *
 * @param  {string}  locale  - Language code of WCAG 2.0  
 * @param  {Object}  nls     - Localized WCAG 2.0 object
 */

OpenAjax.a11y.nls.WCAG20.prototype.addNLS = function (locale, nls) {

  var item;
  var  p,  p_id,  np;  /* WCAG 2.0 Principle */
  var  g,  g_id,  ng;  /* WCAG 2.0 Guideline */
  var sc, sc_id, nsc;  /* WCAG 2.0 Success Criterion */

  // Validate the WCAG 2.0 NLS properties
  if (!nls.abbreviation) OpenAjax.a11y.logger.error("Missing abbreviation property for WCAG 2.0 with locale: " + locale);
  if (!nls.title)  OpenAjax.a11y.logger.error("Missing title property for WCAG 2.0 with locale: "              + locale);
  if (!nls.url)    OpenAjax.a11y.logger.error("Missing url property for WCAG 2.0 with locale: "                + locale);
  if (!nls.levels) OpenAjax.a11y.logger.error("Missing levels property for WCAG 2.0 with locale: "             + locale);
  if (!nls.evaluation_levels) OpenAjax.a11y.logger.error("Missing evaluation_levels property for locale: "     + locale);
  
  var wcag20 = new OpenAjax.a11y.nls.WCAG20NLS(locale, nls.abbreviation, nls.title, nls.url, nls.levels, nls.evaluation_levels);
  
 //  OpenAjax.a11y.logger.debug("WCAG 2.0 " + nls.title + " for " + locale); 
  
  if (!nls.principles || typeof nls.principles !== 'object') {
  
    OpenAjax.a11y.logger.debug("Missing principles object or not at an object for WCAG 2.0 with locale: " + locale);
    return;
    
  } else {
  
    for (p_id in nls.principles) {
    
      p = nls.principles[p_id];
      
//      OpenAjax.a11y.logger.debug("Principle " + p.title + " " + p.id); 
      
      np = new OpenAjax.a11y.nls.WCAG20NLSPrinciple(p_id, p);
      
      for (g_id in p.guidelines) {
      
        g = p.guidelines[g_id];
    
//        OpenAjax.a11y.logger.debug("  Guideline " + g.title + " " + g.id); 
      
        ng = new OpenAjax.a11y.nls.WCAG20NLSGuideline(np, g_id, g);

        for (sc_id in g.success_criteria) {
      
          sc = g.success_criteria[sc_id];
     
          nsc = new OpenAjax.a11y.nls.WCAG20NLSSuccessCriterion(np, ng, sc_id, sc);
          
//          OpenAjax.a11y.logger.debug("    Success Criteria " + nsc.sc_id + " (" + sc_id + "): " + sc.title); 
      
          ng.success_criteria.push(nsc); 
      
        } // end loop
        
        np.guidelines.push(ng); 
        
      } // end loop
      
      wcag20.principles.push(np); 
 
    } // end loop
  }
  
  this.nls[locale] = wcag20;
  
};

/**
 * @method getNLS
 *
 * @memberOf OpenAjax.a11y.nls.WCAG20
 *
 * @desc Returns an object with a localized version of WCAG 2.0 requirements 
 *
 */

OpenAjax.a11y.nls.WCAG20.prototype.getNLS = function() {

  return this.nls[OpenAjax.a11y.locale];
  
};



/* ---------------------------------------------------------------- */
/*                       WCAG20NLS                                     */
/* ---------------------------------------------------------------- */

/**
 * @constructor WCAG20NLS
 *
 * @memberOf OpenAjax.a11y.nls
 *
 * @desc WCAG 2.0 information with properties with localized NLS values 
 *
 * @param  {String}  locale - Language code 
 * @param  {String}  abbrev - Localized abbreviation of WCAG 2.0 guidelines
 * @param  {String}  title  - Localized title of WCAG 2.0 guidelines 
 * @param  {String}  url    - URL to the translation of WCAG 2.0
 * @param  {Object}  levels - WCAG 2.0 levels for success criteria
 *
 * @property  {String}  locale - Language code 
 * @property  {String}  abbrev - Localized abbreviation of WCAG 2.0 guidelines
 * @property  {String}  title  - Localized title of WCAG 2.0 guidelines 
 * @property  {String}  url    - URL to the translation of WCAG 2.0
 * @property  {Object}  levels - WCAG 2.0 levels for success criteria
 *
 * @property  {Array}   principles - Array of WCAG 2.0 principle objects associated with the principle
 */

OpenAjax.a11y.nls.WCAG20NLS = function(locale, abbrev, title, url, levels, evaluation_levels) {

  this.locale = locale;    
  this.abbrev = abbrev;    
  this.title  = title;    
  this.url    = url;  
  this.levels = levels;
  this.evaluation_levels = evaluation_levels;
  
  this.principles = [];
  
};

/**
 * @method getNLSItemById
 *
 * @memberOf OpenAjax.a11y.nls.WCAG20NLS
 *
 * @desc Returns an object with a localized version of WCAG 2.0 requirements 
 *
 * @param {String}  id  -  id for the wcag item to get NLS information
 *
 * @return {Object}  WCAG 2.0 NLS object
 */

OpenAjax.a11y.nls.WCAG20NLS.prototype.getNLSItemById = function(id) {

  for (var i = 0; i < this.principles.length; i++) {
  
     var p = this.principles[i];

//     OpenAjax.a11y.logger.debug("P Compare: " + p.principle_id + " " + id );

     if ((p.id === id) || (p.principle_id === id)) return p;     

     for (var j = 0; j < p.guidelines.length; j++) {
     
       var g = p.guidelines[j];

//       OpenAjax.a11y.logger.debug("  G Compare: " + g.guideline_id + " " + id );

       if ((g.id === id) || (g.guideline_id === id)) return g;
     
       for (var k = 0; k < g.success_criteria.length; k++ ) {
       
         var sc = g.success_criteria[k];

//         OpenAjax.a11y.logger.debug("  SC Compare: " + sc.success_criteria_id + " " + id );

         if ((sc.id === id) || (sc.sc_id === id)) return sc;
       
       } // end loop
     
     } // end loop
     
  } // end loop   
    
  return null;  
};


/**
 * @method getSuccessCriteriaLevel
 *
 * @memberOf OpenAjax.a11y.nls.WCAG20NLS
 *
 * @desc Returns the success criteria 
 *
 * @param {String}  sc_id  -  String representing the success criteria id
 *
 * @return {Number}  Number representing the WCAG 2.0 success level 
 */

OpenAjax.a11y.nls.WCAG20NLS.prototype.getSuccessCriteriaLevel = function (sc_id) {

  var principles = this.principles;

  for (i = 0; i < principles.length; i++) {

    var p = wcag20_nls.principles[i];

    for (j = 0; j < p.guidelines.length; j++) {

      var g = p.guidelines[i];

      for (k = 0; k < g.success_criteria.length; k++) {
      
        var sc = g.success_criteria[i];
        
        if (sc.sc_id === sc_id) return sc.level;

      }
    }
  }

  return OpenAjax.a11y.WCAG20_LEVEL.UNKNOWN;

};



/**
 * @method getNLSWCAG20Level
 *
 * @memberOf OpenAjax.a11y.nls.WCAG20NLS
 *
 * @desc Returns an NLS localized version of WCAG 2.0 success criterion level 
 *
 * @param {Number}  level  -  Numerical constant defined in OAA cache representing the WCAG 2.0 success criterion level
 */

OpenAjax.a11y.nls.WCAG20NLS.prototype.getNLSWCAG20Level = function (level) {

  if (typeof this.levels[level] !== 'string') OpenAjax.a11y.logger.debug("  Level: " + level);


  return this.levels[level];
  
};


/**
 * @method getEvaluationLevelsNLS
 *
 * @memberOf OpenAjax.a11y.nls.WCAG20NLS
 *
 * @desc Returns an NLS localized version of WCAG 2.0 success criterion levels 
 *
 * @param {Number}  levels  -  Numerical constant defined in OAA cache representing the evaluation levels
 *
 * @return {String} String representing the evaluation levels (i.e A, A and AA, A, AA and AAA)
 */

OpenAjax.a11y.nls.WCAG20NLS.prototype.getEvaluationLevelsNLS = function (levels) {

  return this.evaluation_levels[levels];
  
};

/**
 * @method toJSON
 *
 * @memberOf OpenAjax.a11y.nls.WCAG20NLS
 *
 * @desc Returns an nls JSON representation of wcag 2.0 information
 *
 * @param {String} prefix  -  A prefix string typically spaces
 * 
 * @return {String}  JSON formatted string 
 */

OpenAjax.a11y.nls.WCAG20NLS.prototype.toJSON = function(prefix) {

  var next_prefix = "";

  if (typeof prefix !== 'string' || prefix.length === 0) prefix = "";
  else next_prefix = prefix + "  ";
  
  var json = "";
  
  json += "{";
 
  for (var i = 0; i < this.principles.length; i++) json += this.principles[i].toJSON(next_prefix);
  
  json += prefix + "}";

  return json;
};

/* ---------------------------------------------------------------- */
/*                       WCAG20NLSPrinciple                           */
/* ---------------------------------------------------------------- */

/**
 * @constructor WCAG20NLSPrinciple
 *
 * @memberOf OpenAjax.a11y.nls
 *
 * @desc WCAg 2.0 Principle information with properties with localized NLS values 
 *
 * @param  {Object}  principle_id  - Principle id
 * @param  {Object}  info          - Principle information
 *
 * @property  {String}  principle_id  - Principle id 
 * @property  {String}  title         - Title of the principle 
 * @property  {String}  description   - Description of principle 
 * @property  {String}  url_spec      - URL to information on the requirement
 *
 * @property  {Array}   guidelines - Array of WCAG 2.0 guideline objects associated with the principle
 */

OpenAjax.a11y.nls.WCAG20NLSPrinciple = function(principle_id, info) {

  this.principle_id = principle_id;    // Text string
  this.id           = info.id;         // Number
  this.title        = info.title;    
  this.description  = info.description;    
  this.url_spec     = info.url_spec;   
  
  this.guidelines = [];
  
};

/**
 * @method toJSON
 *
 * @memberOf OpenAjax.a11y.nls.WCAG20NLSPrinciple
 *
 * @desc Returns an nls JSON representation of wcag 2.0 principle information
 *
 * @param {String} prefix  -  A prefix string typically spaces
 * 
 * @return {String}  JSON formatted string 
 */

OpenAjax.a11y.nls.WCAG20NLSPrinciple.prototype.toJSON = function(prefix) {

  if (typeof prefix !== 'string' || prefix.length === 0) prefix = "";
  
  var json = "";
       
  json += prefix + "\"" + this.principle_id + "\" : { "; 

  json += prefix + "  \"id\"             : " + this.id + ","; 
  json += prefix + "  \"type\"           : \"p\","; 
  json += prefix + "  \"title\"          : \"" + OpenAjax.a11y.util.escapeForJSON(this.title) + "\","; 
  json += prefix + "  \"description\"    : \"" + OpenAjax.a11y.util.escapeForJSON(this.description) + "\","; 
  json += prefix + "  \"url\"            : \"" + OpenAjax.a11y.util.escapeForJSON(this.url_spec) + "\""; 

  json += prefix + "},"; 
    
  for (var i = 0; i < this.guidelines.length; i++) json += this.guidelines[i].toJSON(prefix);
  
  return json;
};


/* ---------------------------------------------------------------- */
/*                       WCAG20NLSGuideline                           */
/* ---------------------------------------------------------------- */

/**
 * @constructor WCAG20NLSGuideline
 *
 * @memberOf OpenAjax.a11y.nls
 *
 * @desc WCAg 2.0 Guideline information with properties with localized NLS values 
 *
 * @param  {WCAG20NLSPrinciple}  principle     - Principle object reference 
 * @param  {String}              guideline_id  - Guideline ID
 * @param  {Object}              info          - Guideline information object
 *
 * @property  {WCAG20NLSPrinciple}  principle  - Principle object reference 
 *
 * @property  {String}  guideline_id  - Guideline id 
 * @property  {String}  title         - Title of the guideline 
 * @property  {String}  description   - Description of the guideline 
 * @property  {String}  url_spec      - URL to information on the guideline requirement
 *
 * @property  {Array}   success_criteria  - Array of WCAG 2.0 success criteria objects associated with the principle
 */

OpenAjax.a11y.nls.WCAG20NLSGuideline = function(principle, guideline_id, info) {

  this.principle     = principle;    
  
  this.guideline_id  = guideline_id;   
  this.id            = info.id;         // Number
  
  this.title         = info.title;    
  this.description   = info.description;    
  this.url_spec      = info.url_spec;   
  
  this.success_criteria = [];
  
};

/**
 * @method toJSON
 *
 * @memberOf OpenAjax.a11y.nls.WCAG20NLSGuideline
 *
 * @desc Returns an nls JSON representation of wcag 2.0 guideline information
 *
 * @param {String} prefix  -  A prefix string typically spaces
 * 
 * @return {String}  JSON formatted string 
 */

OpenAjax.a11y.nls.WCAG20NLSGuideline.prototype.toJSON = function(prefix) {

  if (typeof prefix !== 'string' || prefix.length === 0) prefix = "";
  
  var json = "";
       
  json += prefix + "\"" + this.guideline_id + "\" : { "; 

  json += prefix + "  \"id\"             : " + this.id + ","; 
  json += prefix + "  \"type\"           : \"g\","; 
  json += prefix + "  \"title\"          : \"" + OpenAjax.a11y.util.escapeForJSON(this.title) + "\","; 
  json += prefix + "  \"description\"    : \"" + OpenAjax.a11y.util.escapeForJSON(this.description) + "\","; 
  json += prefix + "  \"url\"            : \"" + OpenAjax.a11y.util.escapeForJSON(this.url_spec) + "\""; 

  json += prefix + "},"; 
    
  for (var i = 0; i < this.success_criteria.length; i++) json += this.success_criteria[i].toJSON(prefix);
  
  return json;
};



/* ---------------------------------------------------------------- */
/*                       WCAG20NLSSuccessCriterion                    */
/* ---------------------------------------------------------------- */

/**
 * @constructor WCAG20NLSSuccessCriterion
 *
 * @memberOf OpenAjax.a11y.nls
 *
 * @desc  WCAG 2.0 Success Criteria information with properties with localized NLS values 
 *
 * @param  {WCAG20NLSPrinciple}  principle  - Principle object reference 
 * @param  {WCAG20NLSGuideline}  guideline  - Guideline object reference
 * @param  {String}              sc_id      - Success criterion ID
 * @param  {Object}              info       - Success criterion information object
 * 
 * @property  {WCAG20NLSPrinciple}  principle  - Principle object reference 
 * @property  {WCAG20NLSGuideline}  guideline  - Guideline object reference
 *
 * @property  {String}  sc_id          - Success criterion ID
 * @property  {String}  title          - Title of the success criterion 
 * @property  {String}  level          - Level of importance of a success criterion
 * @property  {String}  url_spec       - URL to information on the success criteria requirement
 * @property  {String}  url_meet       - URL to information on how to meet the success criteria requirements
 * @property  {String}  url_understand - URL to information on how to understand the success criteria requirements
 * @property  {Array}   resources      - Other information related to the success criterion
 */

OpenAjax.a11y.nls.WCAG20NLSSuccessCriterion = function(principle, guideline, sc_id, info) {

  this.principle  = principle;    
  this.guideline  = guideline;    
  
  this.sc_id        = sc_id;      // String   
  this.id           = info.id;    // Number
  
  this.level          = info.level;   
  this.title          = info.title;    
  this.description    = info.description;    
  this.url_spec       = info.url_spec;   
  this.url_meet       = info.url_meet;   
  this.url_understand = info.url_understand;   
  
  this.resources = [];  
  
};

/**
 * @method toJSON
 *
 * @memberOf OpenAjax.a11y.nls.WCAG20NLSSuccessCriterion
 *
 * @desc Returns an nls JSON representation of wcag 2.0 success criterion information
 *
 * @param {String} prefix  -  A prefix string typically spaces
 * 
 * @return {String}  JSON formatted string 
 */

OpenAjax.a11y.nls.WCAG20NLSSuccessCriterion.prototype.toJSON = function(prefix) {

  function getNLSLevel(level) {
  
    if (level === OpenAjax.a11y.WCAG20_LEVEL.A) return "A";
    if (level === OpenAjax.a11y.WCAG20_LEVEL.AA) return "AA";
    if (level === OpenAjax.a11y.WCAG20_LEVEL.AAA) return "AAA";
  
    return "unknown";
  }
  
  if (typeof prefix !== 'string' || prefix.length === 0) prefix = "";
  
  var json = "";
       
  json += prefix + "\"" + this.sc_id + "\" : { "; 

  json += prefix + "  \"id\"             : " + this.id + ","; 
  json += prefix + "  \"type\"           : \"sc\","; 
  json += prefix + "  \"level\"          : \"" + getNLSLevel(this.level) + "\","; 
  json += prefix + "  \"title\"          : \"" + OpenAjax.a11y.util.escapeForJSON(this.title) + "\","; 
  json += prefix + "  \"description\"    : \"" + OpenAjax.a11y.util.escapeForJSON(this.description) + "\","; 
  json += prefix + "  \"url\"            : \"" + this.url_spec + "\","; 
  json += prefix + "  \"url_meet\"       : \"" + this.url_meet + "\","; 
  json += prefix + "  \"url_understand\" : \"" + this.url_understand + "\""; 

  if (this.sc_id === '4.1.2') json += prefix + "}"; 
  else json += prefix + "},"; 
    
  return json;
};

/*
 * Copyright 2011-2013 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


/* ---------------------------------------------------------------- */
/*                       Rulesets                                   */
/* ---------------------------------------------------------------- */

/**
 * @constructor Rulesets
 *
 * @memberOf OpenAjax.a11y
 *
 * @desc Information on the rulesets available for evauating documents 
 *
 * @property  {Array}   rulesets - Associative array of rulesets  
 */

OpenAjax.a11y.Rulesets = function() {

  this.rulesets = [];
  
};

/**
 * @method addRuleset
 *
 * @memberOf OpenAjax.a11y.Rulesets
 *
 * @desc Adds a localized version of WCAG 2.0 requirements to the cache 
 *
 * @param  {string}  type        - Type of ruleset (WCAG 2.0 ruleset is the only type currently supported)  
 * @param  {Object}  rulesetdata - JSON object containing the ruleset information
 */

OpenAjax.a11y.Rulesets.prototype.addRuleset = function(type, ruleset_data) {

  switch (type) {
  
  case 'WCAG20':
    var rs = new OpenAjax.a11y.Ruleset(ruleset_data);
    
    if (rs) {
      this.rulesets.push(rs);
      OpenAjax.a11y.logger.info("  Ruleset '" + rs.ruleset_title + "' is loaded");
    }  
    else {
      OpenAjax.a11y.logger.error("  ** Error loading ruleset: " + ruleset_data[id]);
    }  
    break;
    
  default:
    OpenAjax.a11y.logger.error("  ** Rule set type: '" + type + "' not supported");
    break;
  }

};

/**
 * @method getRuleset
 *
 * @memberOf OpenAjax.a11y.Rulesets
 *
 * @desc Gets a ruleset with the specified ID  
 *
 * @param  {string}  ruleset_id  - Ruleset id of the ruleset to return
 */

OpenAjax.a11y.Rulesets.prototype.getRuleset = function(ruleset_id) {

  var i;
  
  for (i = 0; i < this.rulesets.length; i++ ) {
     if (this.rulesets[i].ruleset_id === ruleset_id) return this.rulesets[i]; 
  }

  // if ruleset_id is not defined, return the first ruleset, if it is defined
  if (this.rulesets[0]) return this.rulesets[0];
  
  return null;
};

/**
 * @method getAllRulesets
 *
 * @memberOf OpenAjax.a11y.Rulesets
 *
 * @desc Gets NLS ruleset information for all rulesets  
 *
 * @returns  {Array} Array of objects that contain NLS information about a ruleset
 */

OpenAjax.a11y.Rulesets.prototype.getAllRulesets = function() {

  var rulesets = [];
  
  for (var i = 0; i < this.rulesets.length; i++) {  
    rulesets.push(this.rulesets[i]);
  }

  return rulesets;
};

/**
 * @method toJSON
 *
 * @memberOf OpenAjax.a11y.Rulesets
 *
 * @desc Returns a JSON representation of the rule
 *
 * @param  {String}  prefix     - String of leading spaces for formatting JSON output (Optional) 
 *
 * @return  {String}  Returns a JSON representation of the rule
 */
OpenAjax.a11y.Rulesets.prototype.toJSON = function (prefix) {

  if (typeof prefix !== 'string') prefix = "";

  var json = "";
  
  json += prefix + "[\n";

  var rulesets_len  = this.rulesets.length;
  var rulesets_last = rulesets_len - 1;
  
  for (var i = 0; i < this.rulesets.length; i++) { 
    var ruleset = this.rulesets[i];
    
    json += prefix + "{\n";
    json += prefix + "  \"ruleset_id\"    : \"" + ruleset.ruleset_id      + "\",\n";  
    json += prefix + "  \"version\"       : \"" + ruleset.ruleset_version + "\",\n";
    json += prefix + "  \"last_updated\"  : \"" + ruleset.ruleset_updated + "\",\n";
    json += prefix + "  \"title\"         : \"" + OpenAjax.a11y.util.escapeForJSON(ruleset.ruleset_title)       + "\",\n";
    json += prefix + "  \"author_name\"   : \"" + OpenAjax.a11y.util.escapeForJSON(ruleset.ruleset_author_name) + "\",\n";
    json += prefix + "  \"author_url\"    : \"" + OpenAjax.a11y.util.escapeForJSON(ruleset.ruleset_author_url)  + "\",\n";
    json += prefix + "  \"description\"   : \"" + OpenAjax.a11y.util.escapeForJSON(ruleset.ruleset_description) + "\",\n";
    json += prefix + "  \"rule_mappings\" : {\n";

    var rule_mappings      = ruleset.rule_mappings;
    var rule_mappings_len  = rule_mappings.length;
    var rule_mappings_last = rule_mappings_len - 1;

    for (var j = 0; j < rule_mappings_len; j++) {
      var rule_mapping = rule_mappings[j];
      
      json += prefix + "    \"" + rule_mapping.rule.rule_id + "\" : {\n";
      if (rule_mapping.required) json += prefix + "                \"required\"    : true,\n";
      else json += prefix + "                \"required\"    : false,\n";
      json += prefix + "                \"enabled\" : " + rule_mapping.enabled      + "\n";
      json += prefix + "              }";
      
      if (j != rule_mappings_last) json += ",\n";
      else json += "\n";
    }

    json += prefix + "  }\n";

    json += prefix + "}";
    if (i != rulesets_last) json += ",\n";
  }
  
  json += prefix + "]\n";

  return json;
};



/* ---------------------------------------------------------------- */
/*                       Ruleset                              */
/* ---------------------------------------------------------------- */

/**
 * @constructor Ruleset
 *
 * @memberOf OpenAjax.a11y
 *
 * @param  {Object} ruleset_data  -  JSON object representing rule mapping
 *
 * @example
 *
 * function updateProgess(message, percent) {
 *
 *  .....
 *
 * }
 *
 * var url   = window.location.href;
 * var title = window.title;
 * var doc   = window.document;
 *
 * var ruleset = OpenAjax.a11y.all_rulesets.getRuleset('WCAG20_TRANS');
 * var result  = ruleset.evaluate(url, title, doc, updateProgress, true);
 *
 */ 

/**
 * @private
 * @constructor Internal Properties
 * NOTE: The following properties are defined when the ruleset is loaded
 *
 * @property {String} type                 - String representing the type of ruleset (i.e. WCAG20)          
 * @property {String} ruleset_id           - id of the ruleset           
 * @property {String} ruleset_title        - NLS localized title of the ruleset           
 * @property {String} ruleset_description  - Description of the ruleset         
 * @property {String} ruleset_author_name  - Name of the author(s) or organization         
 * @property {String} ruleset_author_url   - URL to the author(s) or organization       
 * @property {String} ruleset_updated      - Last time the ruleset was updated     
 * @property {Number} number_of_rules      - number of rules in the rule set
 *
 * @property {Array}  rule_mappings - Array of RuleMapping objects          
 *
 * @property {Number}   evaluation_levels          - Level of WCAG 2.0 Success Criteria to evaluate (i.e. A, AA, AAA)
 * @property {Boolean}  recommended_rules_enabled  - If true recommended rules are evaluated
 * @property {Boolean}  website_rules_enabled      - If true rules with scope 'website' are evaluated
 *
 * NOTE: The following properties are defined after each evaluation
 *
 * @property {String} eval_title  - The title of the document evaluated
 * @property {String} eval_url    - The url of the ldocument evaluated
 * @property {String} eval_date   - Date of the evaluation
 *
 * @property {Object} doc          - Reference to browser document object model (DOM) that holds the document to be analyzed
 * @property {Object} wcag20_nls   - Reference to WCAG 2.0 NLS object for current language
 * @property {Object} dom_cache    - Reference to DOMCache object
 *
 * @property {Array}  rule_results   - Array of rule result objects 
 * @property {Array}  rule_mappings  - Array of rule mapping objects (i.e. required or recommended rules) 
 */
 
OpenAjax.a11y.Ruleset = function (ruleset_data) {

  var i;
 
  this.type = "WCAG20";
  this.ruleset_title = {};
  
  this.ruleset_id         = ruleset_data['ruleset_id'];
  this.ruleset_version    = ruleset_data['version'];
  this.evaluation_levels  = OpenAjax.a11y.EVALUATION_LEVELS.A_AA;
  
  this.recommended_rules_enabled = true;
  this.website_rules_enabled = true;

  this.required_count    = 0;
  this.recommended_count = 0;

  this.rule_results = [];

  var id       = ruleset_data['ruleset_id'];
  var title    = "";
  var url      = "";
  var desc     = "";
  var auth     = "";
  var auth_url = "";
  var date     = "0000-00-00";
  var ver      = ruleset_data['version'];

  // Check for ruleset id

  if (ruleset_data['ruleset_id']) {
    this.ruleset_id  = ruleset_data['ruleset_id'];
    OpenAjax.a11y.logger.info("Loading ruleset with the id: " + this.ruleset_id);
  } 
  else {
    OpenAjax.a11y.logger.error("  ** Ruleset missing id");
    return null;
  }

  // Check for ruleset version

  if (ruleset_data['version']) {
    ver  = ruleset_data['version'];
    this.ruleset_version  = ver;    
  } 
  else {
    OpenAjax.a11y.logger.error("  ** Ruleset missing version");
    return null;
  }

  // Check for default and localized ruleset title

  if (ruleset_data.title && ruleset_data.title['default']) {
    title = ruleset_data.title['default'];
    
    // get localized name for ruleset
    
    if (ruleset_data.title[OpenAjax.a11y.locale]) {
      title = ruleset_data.title[OpenAjax.a11y.locale];
    }

    this.ruleset_title = title;

  } 
  else {
    OpenAjax.a11y.logger.error("  ** Ruleset " + this.ruleset_id + " missing default title");
    return null;
  }

  // Check for ruleset last updated property 

  if (ruleset_data['last_updated']) {
    date  = ruleset_data['last_updated'];
    this.ruleset_updated  = date;
  } 
  else {
    OpenAjax.a11y.logger.warning("  ** Ruleset missing last updated date, set to null");
    this.ruleset_updated  = "0000-00-00";
  }

  // Check for default and localized ruleset descriptions

  if (ruleset_data.description && ruleset_data.description['default']) {
    desc = ruleset_data.description['default'];
    
    // get localized name for ruleset
    
    if (ruleset_data.description[OpenAjax.a11y.locale]) {
      desc = ruleset_data.description[OpenAjax.a11y.locale];
    }
    
    this.ruleset_description = desc;
  } 
  else {
    OpenAjax.a11y.logger.error("  ** Ruleset " + this.ruleset_id + " missing default description");
    this.ruleset_description = "no description";
  }

  // Check for default and localized ruleset descriptions

  if (ruleset_data.author && ruleset_data.author.name) {
    auth = ruleset_data.author.name;
    
    if (typeof ruleset_data.author.url === 'string') auth_url = ruleset_data.author.url;

    this.ruleset_author_name = auth;
    this.ruleset_author_url  = auth_url;    

//    OpenAjax.a11y.logger.debug("  Ruleset Author: " + this.ruleset_author_name);
//    OpenAjax.a11y.logger.debug("  Ruleset URL: " + this.ruleset_author_url);
  } 
  else {
    OpenAjax.a11y.logger.error("  ** Ruleset " + this.ruleset_id + " missing author information");
    auth = "no author";
    this.ruleset_author_name = auth;
    this.ruleset_author_url  = "";
  }

  this.number_of_rules   = 0;

  this.rule_mappings = [];

  var rule_mappings_from_data = ruleset_data['rule_mappings'];
  
  var rm_new;

  if (rule_mappings_from_data) {

    for (var rule_id in rule_mappings_from_data) {
  
      var rule_mapping = rule_mappings_from_data[rule_id];
  
      if (typeof rule_mapping.required === 'boolean') {
    
        if (typeof rule_mapping.enabled === 'boolean') {
          rm_new = new OpenAjax.a11y.RuleMapping(rule_id, rule_mapping.required, rule_mapping.enabled);
        } 
        else { 
          rm_new = new OpenAjax.a11y.RuleMapping(rule_id, rule_mapping.required, true);
        }
        
        if (rm_new && rm_new.rule) {
          this.rule_mappings.push(rm_new);
      
          this.number_of_rules++;

          if (rule_mapping.required) this.required_count++;   
          else this.recommended_count++;   

        }
      } 
      else {
        OpenAjax.a11y.logger.error("        ** Ruleset rule " + rule_id + " is missing valid 'type' property");              
      }    
    } // end loop
  }
  else {
    OpenAjax.a11y.logger.info("  ** Ruleset " + this.ruleset_id + " does not have any rules");
  }
  
  // local references to current NLS information, based on current locale setting
  
  this.wcag20_nls = OpenAjax.a11y.all_wcag20_nls.getNLS();      

  this.ruleset_information = OpenAjax.a11y.info.RulesetInfo(id, title, url, desc, this.required_count, this.recommended_count, auth, auth_url, date, ver);

  return this;
  
};

/**
 * @method getRulesetInfo
 *
 * @memberOf OpenAjax.a11y.Ruleset
 *
 * @desc Return ruleset of information 
 *
 * @return {RulesetInfo}  RulesetInfo object
 */
 
OpenAjax.a11y.Ruleset.prototype.getRulesetInfo = function () {
  return this.ruleset_information;
};

/**
 * @method getRulesetId
 *
 * @memberOf OpenAjax.a11y.Ruleset
 *
 * @desc Return id of the ruleset 
 *
 * @return {String}  String representing the ruleset ID
 */
 
OpenAjax.a11y.Ruleset.prototype.getRulesetId = function () {
  return this.ruleset_id;
};

/**
 * @method getRulesetTitle
 *
 * @memberOf OpenAjax.a11y.Ruleset
 *
 * @desc Return title of the ruleset
 *
 * @return {String}  String representing the NLS title
 */
 
OpenAjax.a11y.Ruleset.prototype.getRulesetTitle = function () {
  return this.ruleset_title;
};

/**
 * @method getRulesetVersion
 *
 * @memberOf OpenAjax.a11y.Ruleset
 *
 * @desc Return version of the ruleset
 *
 * @return {String}  String representing the version
 */
 
OpenAjax.a11y.Ruleset.prototype.getRulesetVersion = function () {
  return this.ruleset_version;
};

/**
 * @method getRulesetDescription
 *
 * @memberOf OpenAjax.a11y.Ruleset
 *
 * @desc Return the description of the ruleset
 *
 * @return {String}  String representing the version
 */
 
OpenAjax.a11y.Ruleset.prototype.getRulesetDescription = function () {
  return this.ruleset_description;
};

/**
 * @method getRulesetAuthor
 *
 * @memberOf OpenAjax.a11y.Ruleset
 *
 * @desc Return the author of the ruleset
 *
 * @return {String}  String representing the author
 */
 
OpenAjax.a11y.Ruleset.prototype.getRulesetAuthor = function () {
  return this.ruleset_author_name;
};

/**
 * @method getRulesetAuthorURL
 *
 * @memberOf OpenAjax.a11y.Ruleset
 *
 * @desc Return the author url of the ruleset
 *
 * @return {String}  String representing the author url
 */
 
OpenAjax.a11y.Ruleset.prototype.getRulesetAuthorURL = function () {
  return this.ruleset_author_url;
};


/**
 * @method getNumberOfRequiredRules
 *
 * @memberOf OpenAjax.a11y.Ruleset
 *
 * @desc Return number of required rules
 *
 * @return {Number}  Number of required rules in the ruleset
 */
 
OpenAjax.a11y.Ruleset.prototype.getNumberOfRequiredRules = function () {
  return this.required_count;
};

/**
 * @method getNumberOfRecommendedRules
 *
 * @memberOf OpenAjax.a11y.Ruleset
 *
 * @desc Return number of recommended rules
 *
 * @return {Number}  Number of recommended rules in the ruleset
 */
 
OpenAjax.a11y.Ruleset.prototype.getNumberOfRecommendedRules = function () {
  return this.recommended_count;
};

/**
 * @method getNumberOfRules
 *
 * @memberOf OpenAjax.a11y.Ruleset
 *
 * @desc Return total number of rules
 *
 * @return {Number}  Total number of rules in the ruleset
 */
 
OpenAjax.a11y.Ruleset.prototype.getNumberOfRules = function () {
  return this.number_of_rules;
};

/**
 * @method setWebsiteRulesEnabled
 *
 * @memberOf OpenAjax.a11y.Ruleset
 *
 * @desc Enable and disable evaluation of rules with scope 'website' 
 *
 * @param  {Boolean}  enable - If true enable rules of scope website to be executed
 */
 
OpenAjax.a11y.Ruleset.prototype.setWebsiteRulesEnabled = function (enable) {

  this.website_rules_enabled = enable;
  
};

/**
 * @method getWebsiteRulesEnabled
 *
 * @memberOf OpenAjax.a11y.Ruleset
 *
 * @desc Enable and disable evaluation of rules with scope 'website' 
 *
 * @return  {Boolean}  If true rules of scope website are being evaluated, otherwise rules not evaluated
 */
 
OpenAjax.a11y.Ruleset.prototype.getWebsiteRulesEnabled = function () {

  return this.website_rules_enabled;
  
};


/**
 * @method setEvaluationLevels
 *
 * @memberOf OpenAjax.a11y.Ruleset
 *
 * @desc Enable and disable rules based on the WCAG level 
 *
 * @param  {Number}    level   - Level to success criteria to test
 */
 
OpenAjax.a11y.Ruleset.prototype.setEvaluationLevels = function (level) {

  this.evaluation_levels = level;
  
};

/**
 * @method getEvaluationLevelsConstant
 *
 * @memberOf OpenAjax.a11y.Ruleset
 *
 * @desc Get WCAG levels to evaluate 
 *
 * @return  {Number}  Number represents the levels of success criteria to test
 */
 
OpenAjax.a11y.Ruleset.prototype.getEvaluationLevelsConstant = function () {

  return this.evaluation_levels;
  
};

/**
 * @method getEvaluationLevels
 *
 * @memberOf OpenAjax.a11y.Ruleset
 *
 * @desc Get NLS WCAG levels to evaluate 
 *
 * @return  {String}  String representing the levels of success criteria evaluated
 */
 
OpenAjax.a11y.Ruleset.prototype.getEvaluationLevels = function () {

  var nls_wcag20  = OpenAjax.a11y.all_wcag20_nls.getNLS();  

  return nls_wcag20.getEvaluationLevelsNLS(this.evaluation_levels);
  
};


/**
 * @method setRecommendedRulesEnabled
 *
 * @memberOf OpenAjax.a11y.Ruleset
 *
 * @desc Enable and disable evaluation of recommeneded rules 
 *
 * @param  {Boolean}    enabled   - True to evaluate recommended rules, False if not to evaluated recommeneded rules
 */
 
OpenAjax.a11y.Ruleset.prototype.setRecommendedRulesEnabled = function (enabled) {

  this.recommended_rules_enabled = enabled;
  
};

/**
 * @method getRecommendedRulesEnabled
 *
 * @memberOf OpenAjax.a11y.Ruleset
 *
 * @desc Get the state of the evaluation of recommeneded rules 
 *
 * @return  {Boolean} True if evaluation included recommended rules, False if not to evaluate recommeneded rules
 */
 
OpenAjax.a11y.Ruleset.prototype.getRecommendedRulesEnabled = function () {

  return this.recommended_rules_enabled;
  
};


/**
 * @method setBrokenLinkTesting
 *
 * @memberOf OpenAjax.a11y.Ruleset
 *
 * @desc Enable and disable the cache from testing for broken urls 
 *
 * @param  {Boolean}  broken_links   - If true enables cache to test for broken links
 */
 
OpenAjax.a11y.Ruleset.prototype.setBrokenLinkTesting = function (broken_links) {
  
  OpenAjax.a11y.URL_TESTING_ENABLED = broken_links;
  
};

/**
 * @method getBrokenLinkTesting
 *
 * @memberOf OpenAjax.a11y.Ruleset
 *
 * @desc Get whether broken link testing is enabled or disabled  
 *
 * @return  {Boolean}  True if broken link testing is enabled
 */
 
OpenAjax.a11y.Ruleset.prototype.getBrokenLinkTesting = function () {
  
  return OpenAjax.a11y.URL_TESTING_ENABLED;
  
};

/**
 * @method setDataTableAssumption
 * @deprecated
 *
 * @memberOf OpenAjax.a11y.Ruleset
 *
 * @desc Set whether table markup should be assumed to be used for layout or tabular data
 *       The assumption is used when no indicators of layout (i.e. role="presentation") or
 *       tabular data (i.e. no TH cells, summary attributes, headers attributes ....) are
 *       found in the markup 
 *
 * @param  {Boolean}  assumption   - If false asssumes tables are used for layout; otherwise assumes tabular data 
 */
 
OpenAjax.a11y.Ruleset.prototype.setDataTableAssumption = function (assumption) {
  
  OpenAjax.a11y.DATA_TABLE_ASSUMPTION = assumption;
  
};

/**
 * @method getDataTableAssumption
 * @deprecated
 *
 * @memberOf OpenAjax.a11y.Ruleset
 *
 * @desc Get whether table markup should be assumed to be used for layout or tabular data 
 *       The assumption is used when no indicators of layout (i.e. role="presentation") or
 *       tabular data (i.e. no TH cells, summary attributes, headers attributes ....) are
 *       found in the markup 
 *
 * @return  {Boolean}  if false table markup is assumed to be for layout; if true the assumption is tabular data 
 */
 
OpenAjax.a11y.Ruleset.prototype.getDataTableAssumption = function () {
  
  return OpenAjax.a11y.DATA_TABLE_ASSUMPTION;
  
};


/**
 * @method setLayoutTableAssumption
 *
 * @memberOf OpenAjax.a11y.Ruleset
 *
 * @desc Set whether table markup should be assumed to be used for layout or tabular data
 *       The assumption is used when no indicators of layout (i.e. role="presentation") or
 *       tabular data (i.e. no TH cells, summary attributes, headers attributes ....) are
 *       found in the markup 
 *
 * @param  {Boolean}  assumption   - If true asssumes table markup is used for layout; if false  unless header cells or other indicator of a data table is found
 */
 
OpenAjax.a11y.Ruleset.prototype.setLayoutTableAssumption = function (assumption) {
  
  OpenAjax.a11y.DATA_TABLE_ASSUMPTION = !assumption;
  
};

/**
 * @method getLayoutTableAssumption
 *
 * @memberOf OpenAjax.a11y.Ruleset
 *
 * @desc Set whether table markup should be assumed to be used for layout or data table 
 *       The assumption is used when no indicators of layout (i.e. role="presentation") or
 *       tabular data (i.e. no TH cells, summary attributes, headers attributes ....) are
 *       found in the markup 
 *
 * @return  {Boolean}  if true table markup is assumed to be for layout; if false the table markup is assumed to be for tabular data 
 */
 
OpenAjax.a11y.Ruleset.prototype.getLayoutTableAssumption = function () {
  
  return !OpenAjax.a11y.DATA_TABLE_ASSUMPTION;
  
};



/**
 * @method setEventHandlerProcessor
 *
 * @memberOf OpenAjax.a11y.Ruleset
 *
 * @desc Set which process
 *
 * @param  {String}  option   - The event handler process to use to identify events on a DOM element
 *                              Options currently implemented:
 *                              firefox
 *                              fae-util
 * 
 *                              Any other option will result in no event handler processing
 */
 
OpenAjax.a11y.Ruleset.prototype.setEventHandlerProcessor = function (option) {
  
  option = option.toLowerCase();
  
  switch (option) {
  
  case 'firefox':
  
    OpenAjax.a11y.EVENT_HANDLER_PROCESSOR = option;
    break;
    
  case 'fae-util':

    OpenAjax.a11y.EVENT_HANDLER_PROCESSOR = option;
    break;
  
  default:
    
    OpenAjax.a11y.EVENT_HANDLER_PROCESSOR = "none";
    break;
  
  }
  
};

/**
 * @method getEventHandlerProcessor
 *
 * @memberOf OpenAjax.a11y.Ruleset
 *
 * @desc Get the current option for event handler processining
 *
 * @return  {String}  Returns the current setting of the option for even handler processing
 */
 
OpenAjax.a11y.Ruleset.prototype.getEventHandlerProcessor = function () {
  
  return OpenAjax.a11y.EVENT_HANDLER_PROCESSOR;
  
};

/**
 * @method evaluate
 *
 * @memberOf OpenAjax.a11y.Ruleset
 *
 * @desc Evaluate a document using the OpenAjax ruleset and return an evaluation object 
 *
 * @param  {String}    url       - url of document being analyzed    
 * @param  {String}    title     - Title of document being analyzed    
 * @param  {Object}    doc       - Browser document object model (DOM) to be evaluated    
 *
 * @param  {Object}    progress_callback  - Progress call back function (NOTE: not used right now)    
 * @param {Boolean} build_cache When true will build all cache in one tranversal of the DOMElements cache, when false specialized caches will be built when they are need by a rule
 *
 */
 
OpenAjax.a11y.Ruleset.prototype.evaluate = function (url, title, doc, progress_callback, build_cache) {
      
  var PROGRESS = OpenAjax.a11y.PROGRESS;

  // OpenAjax.a11y.logger.debug("Starting evaluation: " + this.ruleset_id + " " + this.default_name + " " + this.number_of_rules + " rules" );
  
  var dom_cache = new OpenAjax.a11y.cache.DOMCache(url, title, doc);      

  dom_cache.updateDOMElementCache();
  
  if (build_cache) { 
    dom_cache.updateAllCaches();
  }  

  var evaluation_result = new OpenAjax.a11y.EvaluationResult(this, title, url, doc, dom_cache);

  var rule_mappings = this.rule_mappings;
  var rule_mappings_len = rule_mappings.length;

  OpenAjax.a11y.logger.info("Starting evaluation....");
  OpenAjax.a11y.logger.info("    URL: " + url);  
  OpenAjax.a11y.logger.info("RULESET: " + this.ruleset_id);  

  for (var i = 0; i < rule_mappings_len; i++) {
    
    var rule_mapping = rule_mappings[i];
    var rule = rule_mapping.rule;
    var rule_definition  = rule.getRuleDefinition(rule_mapping.required);                     

    if (rule_mapping) {

      var rule_result = new OpenAjax.a11y.RuleResult(rule_mapping); 

//      OpenAjax.a11y.logger.debug("Recommended Rules Enabled: " + this.recommended_rules_enabled);
//      OpenAjax.a11y.logger.debug("Rule: " + rule.rule_id + "  Enabled: " + rule_mapping.enabled  + "  Required: " + rule_mapping.required + "  WCAG 2.0 Level: " + rule.getWCAG20LevelConstant() + " Ruleset level: " + this.evaluation_levels + " test: " + (rule.getWCAG20LevelConstant() & this.evaluation_levels));

      if (rule &&
          rule_mapping.enabled && 
          (rule_mapping.required ||
           this.recommended_rules_enabled) &&
          (!rule.isScopeWebsite() || this.website_rules_enabled) && 
          (rule.getWCAG20LevelConstant() & this.evaluation_levels)) {      

        rule_result.rule_evaluated = true;

        // Check to see if the specialized cache needed for the rule is already 
        // If not create the specialized cache
                     
        if (!build_cache ) {
          var up_to_date = dom_cache.isUpToDate(rule.cache_dependency);
          if (up_to_date.exists) {
            if(!up_to_date.up_to_date) dom_cache.updateCache(rule.cache_dependency);
          } 
        } 

        if (rule.language_dependency.length) {
          // Rules with a language restriction
          if (rule.language_dependency.indexOf(OpenAjax.a11y.locale) >= 0) {
            rule.validate(dom_cache, rule_result);
          }
        }
        else {
          // Rules without any language restrictions
          rule.validate(dom_cache, rule_result);
        }   
   
        evaluation_result.rule_results.push(rule_result);
        
        var nrs = rule_result.getNodeResultSummary();
        
        evaluation_result.node_result_summary.addNodeResultSummary(nrs);
        
        evaluation_result.rule_results_summary.addRuleResult(rule_result);
        
      }
      
      // OpenAjax.a11y.logger.debug("Aggregating rule Results: " + rule_result);

    }                 
  } // end rule loop
          
  return evaluation_result;
  
};


/**
 * @method toJSON
 *
 * @memberOf OpenAjax.a11y.Ruleset
 *
 * @desc Creates a JSON representation of the rules in the ruleset 
 *
 * @param  {String}  prefix         - A prefix string typically spaces for formatting output
 * @param  {Number}  rule_category  - Number representing the rule categories to include 
 *
 * @return {String} JSON formatted string representing the ruleset
 */
 
OpenAjax.a11y.Ruleset.prototype.toJSON = function (prefix, rule_category) {
  
  function referencesToJSON(name, refs, last) {
  
    var title = "";
    var url = "";

    var refs_len = refs.length;
    var refs_last = refs_len - 1;
      
    if (refs_len > 0) {
      json += next_prefix + "  \"" + OpenAjax.a11y.util.escapeForJSON(name) + "\" : [\n"; 
      for (var i = 0; i < refs_len; i++) {
        var ref = refs[i];
        
        if (typeof ref === 'string') {
           title = ref;
           url = "";
        }
        else {
           title = ref.title;
           url = ref.url;
        }
        
        if (i === refs_last) json += next_prefix_2 + "{ \"title\" : \"" + OpenAjax.a11y.util.escapeForJSON(title) + "\", \"url\" : \"" + OpenAjax.a11y.util.escapeForJSON(url) + "\"}\n";
        else json += next_prefix_2 + "{ \"title\" : \"" + OpenAjax.a11y.util.escapeForJSON(title) + "\", \"url\" : \"" + OpenAjax.a11y.util.escapeForJSON(url) + "\"},\n";
      }       
      json += next_prefix + "  ]"; 
    }
    else {
      json += next_prefix + "  \"purpose\"    : []";       
    }
    
    if (typeof last === 'undefined' || !last) json += ',\n';
    else json += '\n';
    
  } // end function
    
  var next_prefix = "";
  var next_prefix_2 = "";

  if (typeof prefix !== 'string' || prefix.length === 0) {
    prefix = "";
  }  
  else {
    next_prefix   = prefix + "    ";  
    next_prefix_2 = prefix + "        ";  
  }
  
  var json = "";
  
  json += "{\n";

  json += prefix + "  \"ruleset_id\"          : \"" + this.ruleset_id + "\",\n";
  json += prefix + "  \"ruleset_title\"       : \"" + OpenAjax.a11y.util.escapeForJSON(this.getRulesetTitle()) + "\",\n";
  json += prefix + "  \"ruleset_description\" : \"" + OpenAjax.a11y.util.escapeForJSON(this.getRulesetDescription()) + "\",\n";
  json += prefix + "  \"ruleset_author_name\" : \"" + OpenAjax.a11y.util.escapeForJSON(this.getRulesetAuthor()) + "\",\n";
  json += prefix + "  \"ruleset_author_url\"  : \"" + this.ruleset_author_url + "\",\n";
  json += prefix + "  \"ruleset_updated\"     : \"" + this.ruleset_updated + "\",\n";
  json += prefix + "  \"wcag20_level\"        : "  + this.wcag20_level + ",\n";
  json += prefix + "  \"rec_rules_enabled\"   : "  + this.recommended_rules_enabled + ",\n";


  var rule_mappings = this.rule_mappings;
  var rule_mappings_len = rule_mappings.length;

  if (rule_mappings_len > 0) {
    json += prefix + "  \"rule_mappings\" : {\n";
    

    for (var i = 0; i < rule_mappings_len; i++) {
    
      var rule_mapping = rule_mappings[i];
      var rule = rule_mapping.rule;
      var rule_definition  = rule.getRuleDefinition(rule_mapping.required);

      json += next_prefix + "\"" + rule.rule_id + "\" : {\n"; 
      json += next_prefix + "  \"enabled\"          : "  + rule_mapping.enabled + ",\n"; 
      json += next_prefix + "  \"required\"         : "  + rule_mapping.required + ",\n";
      json += next_prefix + "  \"scope_element\"    : "  + rule.isScopeElement() + ",\n"; 
      json += next_prefix + "  \"nls_scope\"        : \"" + rule.getRuleScopeNLS() + "\",\n"; 
      json += next_prefix + "  \"wcag_primary_id\"  : \"" + rule.getPrimarySuccessCriterion() + "\",\n"; 
      json += next_prefix + "  \"wcag_related_ids\" : \"" + rule.getRelatedSuccessCriteria() + "\",\n"; 
      json += next_prefix + "  \"definition\"       : \"" + OpenAjax.a11y.util.escapeForJSON(rr.getRuleDefinition(rule_mapping.required)) + "\",\n"; 
      json += next_prefix + "  \"summary\"          : \"" + OpenAjax.a11y.util.escapeForJSON(rr.getRuleSummary(rule_mapping.required)) + "\",\n"; 
      
      referencesToJSON('purpose', rr.getPurpose());
      referencesToJSON('techniques', rr.getTechniques());
      referencesToJSON('informational_links', rr.getInformationalLinks(), true);

      if (i === last) json += next_prefix + "}\n";
      else json += next_prefix + "},\n";      
    }  // end loop
    
   json += prefix + "  }\n";
   
  }
  else {
    json += prefix + "  \"rule_mappings\" : {}\n";
  }
  
  json += prefix + "}\n";
 
  return json;
    
};


/* ---------------------------------------------------------------- */
/*                       RuleMapping                          */
/* ---------------------------------------------------------------- */

/**
 * @constructor RuleMapping
 *
 * @memberOf OpenAjax.a11y
 *
 * @desc  Contains information about a rule in a ruleset
 *
 * @param  {String}   rule_id   - id of the rule
 * @param  {Boolean}  required  - Boolean indicating if the rule is required or 
 *                                recommended
 * @param  {Boolean}  enabled   - Initial value for the enabled property
 *
 * @property  {Object}   rule      - rule object 
 * @property  {Boolean}  required  - Boolean indicating if the rule is required or 
 *                                   recommended
 * @property  {Boolean}  enabled   - True if the rule is enabled, otherwise false
 * 
 */
 

OpenAjax.a11y.RuleMapping = function (rule_id, required, enabled) {

   this.rule         = null;
   this.required     = required;
   this.enabled      = enabled;

   var r = OpenAjax.a11y.all_rules.getRuleByRuleId(rule_id);
   
   if (r) {
     this.rule = r;
   }
   else {
     OpenAjax.a11y.logger.error("  ** Rule with rule id='" + rule_id + "' does not exist!");   
   }
   
};



/*
 * Copyright 2011 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* ---------------------------------------------------------------- */
/*                      Logger APIs                                 */ 
/* ---------------------------------------------------------------- */

/**
 * @object logger
 *
 * @memberOf OpenAjax.a11y
 *
 * @desc logger object supports sending messages to the console
 *       This default logger object does nothing other than enumerate
 *       the minimal interface that must be implemented for logging
 *       Use the setLogger function to replace the default object with an
 *       object that supports logging in your development environment
 *       The replacement object may have additional methods defined by 
 *       the host for controlling logging, but the OpenAjax Evaluation 
 *       library will only use these 4 methods.  
 */

OpenAjax.a11y.logger = OpenAjax.a11y.logger || {
  debug: function (message) {},
  info:  function (message) {},
  warn:  function (message) {},
  error: function (message) {}
};

OpenAjax.a11y.setLogger = function (logger) {
   OpenAjax.a11y.logger = logger;
};

/* ---------------------------------------------------------------- */
/*                   OpenAjax High Level APIs                       */ 
/* ---------------------------------------------------------------- */

// basic info about version of ruleset and rules
OpenAjax.a11y.name = "OpenAjax Alliance Accessibility Tools Task Force";
OpenAjax.a11y.version = "2.0.0";
OpenAjax.a11y.baseUri = "http://www.openajax.org/member/wiki/Accessibility";
    

/*
 * OpenAjax registration information 
 */

if (OpenAjax && OpenAjax.hub) {
  OpenAjax.hub.registerLibrary(this.name, this.baseUri, this.version);
}

/**
 * @object locale
 *
 * @memberOf OpenAjax.a11y
 *
 * @type String
 *
 * @default en-us
 *
 * @desc Current locale messages should use for generating text 
 */

OpenAjax.a11y.locale = "en-us";


/**
 * @object all_rules
 *
 * @memberOf OpenAjax.a11y
 *
 * @type Rules
 *
 * @desc Object containing data and methods related to rules
 */

OpenAjax.a11y.all_rules = OpenAjax.a11y.all_rules || new OpenAjax.a11y.Rules();

/**
 * @object cache_nls
 *
 * @memberOf OpenAjax.a11y
 *
 * @type CacheNLS
 *
 * @desc Object containing data and methods for generating human readable text for cache item properties and values
 */

OpenAjax.a11y.cache_nls = OpenAjax.a11y.cache_nls || new OpenAjax.a11y.nls.Cache();

/**
 * @object all_wcag20_nls
 *
 * @memberOf OpenAjax.a11y
 *
 * @type WCAG20NLS
 *
 * @desc Object containing data and methods related to localized versions of WCAG 2.0 requirements 
 */

OpenAjax.a11y.all_wcag20_nls = OpenAjax.a11y.all_wcag20_nls || new OpenAjax.a11y.nls.WCAG20();

/**
 * @function all_rulsets
 *
 * @memberOf OpenAjax.a11y
 *
 * @type Object
 *
 * @desc Object containing data and methods related to rulesets available for evaluation 
 */

OpenAjax.a11y.all_rulesets   = OpenAjax.a11y.all_rulesets   || new OpenAjax.a11y.Rulesets();
/*
 * Copyright 2011-2013 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* ---------------------------------------------------------------- */
/*         Initialize HMTL Report Content, Scripts and CSSS         */
/* ---------------------------------------------------------------- */

/**
 * Initialize css and scripts for report files
 */
 
OpenAjax.a11y.report_css                     = '  <style type="text/css">\n  <!-- Not initialized --> \n  </style>\n';

OpenAjax.a11y.report_element_type_view_js    = '  <script type="text/javascript">\n  <!-- Not initialized --> \n  </script>\n';
OpenAjax.a11y.report_element_type_view_body  = '  <body>\n  <!-- Not initialized --> \n  </body>\n';

OpenAjax.a11y.report_rule_category_view_js   = '  <script type="text/javascript">\n  <!-- Not initialized --> \n  </script>\n';
OpenAjax.a11y.report_rule_category_view_body = '  <body>\n  <!-- Not initialized --> \n  </body>\n';

OpenAjax.a11y.report_rule_summary_view_js    = '  <script type="text/javascript">\n  <!-- Rule Report Summary not initialized --> \n  </script>\n';
OpenAjax.a11y.report_rule_summary_view_body  = '  <body>\n  <!-- Rule Report Summary not initialized --> \n  </body>\n';

OpenAjax.a11y.rule_export_body       = '  <body>\n  <!-- Rule Export HTML code not initialized --> \n  </body>\n';
OpenAjax.a11y.rule_export_js         = '  <script type="text/javascript">\n  <!-- Rule Export Javascript not initialized --> \n  </script>\n';
OpenAjax.a11y.rule_export_css        = '  <style type="text/css">\n  <!-- Rule Export CSS not initialized --> \n  </style>\n';

/**
 * Copyright 2011-2013 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* -------------------------------------------------------------------------------------- */
/* OpenAjax Alliance Cache Properties and Values National Language Support (NLS): English */
/* -------------------------------------------------------------------------------------- */
   

OpenAjax.a11y.cache_nls.addCacheNLSFromJSON('en-us', {

    /*
     * Boolean values 
     */
    boolean_values : {
     true_value  : 'True',
     false_value : 'False'
    }, 

    not_boolean_value : "Not a boolean value",

    yes_no_values : {
     yes_value : 'Yes',
     no_value  : 'No'
    }, 

    required    : 'Required',
    recommended : 'Recommended',
    
    filteredItem  : "filtered element",
    filteredItems : "filtered elements",

    filteredRule  : "filtered rule",
    filteredRules : "filtered rules",
    
    undefined : "undefined",
    empty     : "empty",
    
    message   : "Message",
    
    rules     : "rules",
    rule      : "rule",

    noAction  : "No action needed",

    /*
     * The types of ways a rule can be included in a ruleset
     */
    rule_categories: [
    {
      id    : OpenAjax.a11y.RULE_CATEGORIES.AUDIO_VIDEO,
      title : 'Audio and Video Rules',
      url   : '',
      desc  : 'The audio and video rules evaluate the object, audio and video elements on a page for text transcripts, captions and audio descriptions.'
    },
    {
      id    : OpenAjax.a11y.RULE_CATEGORIES.DATA_TABLES,
      title : 'Table Rules',
      url   : '',
      desc  : 'The rules for table related elements includings table, thead, tbody, tr, th and td elements ann their attirbutes to make simple and complex data tables accessible.'
    },
    {
      id    : OpenAjax.a11y.RULE_CATEGORIES.FORM_CONTROLS,
      title : 'Form Rules',
      url   : '',
      desc  : 'The form control rules include the labeling of form controls and error feedback.'
    },
    {
      id    : OpenAjax.a11y.RULE_CATEGORIES.IMAGES,
      title : 'Image Rules',
      url   : '',
      desc  : 'The image rules include the use and describing of img and area elements in a web page.'
    },
    {
      id    : OpenAjax.a11y.RULE_CATEGORIES.KEYBOARD_SUPPORT,
      title : 'Keyboard Support Rules',
      url   : '',
      desc  : ''
    },
    {
      id    : OpenAjax.a11y.RULE_CATEGORIES.LINKS,
      title : 'Link Rules',
      url   : '',
      desc  : 'The link rules include the use of the a and area elements for accessible links on a web page.'
    },
    {
      id    : OpenAjax.a11y.RULE_CATEGORIES.NAVIGATION_FINDABILITY,
      title : 'Site Navigation Rules',
      url   : '',
      desc  : 'The site navigation rules are for finding content on web pages within websites.'
    },
    {
      id    : OpenAjax.a11y.RULE_CATEGORIES.STRUCTURE_CONTENT,
      title : 'Structure and Content Rules',
      url   : '',
      desc  : 'The rules related to titling, headers, ARIA landmarks, lists, abbreviations and language.'
    },
    {
      id    : OpenAjax.a11y.RULE_CATEGORIES.STYLES_READABILITY,
      title : 'Styling and Readability Rules',
      url   : '',
      desc  : 'The color and styling rules include color contrast of text and the use of color to display information on a web page.'
    },
    {
      id    : OpenAjax.a11y.RULE_CATEGORIES.WIDGETS_SCRIPTS,
      title : 'Widget and Scripting Rules',
      url   : '',
      desc  : 'The widget rules evaluate the use of event handlers on elements other than form controls for the creation of interactive elements.  Interactive elements created this way need to support the keyboard and have ARIA markup to describe the widgets and their proeprties and states.'
    },
    // Composite rule categories
    {
      id    : OpenAjax.a11y.RULE_CATEGORIES.ALL,
      title : 'All Rules',
      url   : '',
      desc  : 'The all rule category includes all the rules in the ruleset and provides a way to sort and compare the results of all the rules.'
    },
    {
      id    : OpenAjax.a11y.RULE_CATEGORIES.EVALUATION_RESULTS,
      title : 'All Evaluation Results',
      url   : '',
      desc  : 'Evaluation results include all rule results and do not filter out any node results.'
    }
    ],

    /*
     * Relative implementation priorities of complying to rule requirements
     */
    priorities: ['Undefined', 'First Priority', 'Second Priority', 'Third Priority'],

    /*
     * Types of rule references to a requirement
     */
    references: ['Unknown', 'Requirement', 'Coding Technique', 'Manual Evaluation', 'Best Practice', 'Authoring Technique', 'Other'],

    /*
     * Abbreviation for the types of rule references to a requirement
     */
    reference_abbreviations: ['U', 'R', 'C', 'ME', 'BP', 'A', 'O'],

    /*
     * Media constant values
     */
    reference_media_contants: ['Undefined', 'No', 'Maybe', 'Yes'],
    
    missing_label : {
      label : "no label",
      style : "missing_label"
    },  
    
    empty_alt_text : {
      label : "empty alt",
      style : "empty_alt"
    },

    missing_alt : {
      label : "missing alt attribute",
      style : "empty_alt"
    },
    
    invalid_attribute : {
      label : "invalid attribute",
      value : "",
      style : "invalid"
    },

    invalid_value : {
      label : "",
      value : " (invalid)",
      style : "invalid"
    },

    /**
     * Result types for the evaluation of a rule 
     */
    rule_types: [ { label       : 'None', 
                    abbrev      : 'none', 
                    description : 'No rule results',
                    style       : 'none'
                  },
                  { label       : 'Not Applicable', 
                    abbrev      : 'na', 
                    description : 'Rule did not apply',
                    style       : 'not_applicable'
                  },
                  { label       : 'Passed', 
                    abbrev      : 'P', 
                    description : 'Passed a required or recommended rule',
                    style       : 'passed'
                  },
                  { label       : 'Hidden', 
                    abbrev      : 'H', 
                    description : 'Element is hidden from users and was not evaluted for accessibility',
                    style       : 'hidden'
                  },
                  { label       : 'Manual Check', 
                    abbrev      : 'MC', 
                    description : 'The rule requires human inspection and judgement to determine if the requirements of the rule has been met',
                    style       : 'manual_check'
                  },
                  { label       : 'Warning', 
                    abbrev      : 'W', 
                    description : 'Failure of a recommended rule',
                    style       : 'warning'
                  },
                  { label      : 'Violation', 
                    abbrev      : 'V', 
                    description : 'Failure of a required rule',
                    style       : 'violation'
                  }
                  ],  
                  
    /*
     * Status of a rule for evaluating a requirement
     */
    status: ['Undefined', 'Proposed', 'Accepted', 'Deprecated'],

    resource_properties : {

    /*
     * DOMElement object properties
     */

      'document_order' : {
        label       : 'Order',
        description : 'The ordinal position of the item in the list',
        style       : 'doc_order'
      },
      'tag_name' : {
        label       : 'Tag Name',
        description : 'Tag (or element) name of the item',
        style       : 'element'
      },
      'id' : { 
        label       : 'id',
        description :  'Value of the id attribute'
      },
      'id_unique'   : { 
        label       : 'ID unique',
        description :  'Information about the id attribute value',
        values      : ['Undefined value', 'Not defined', 'Unique', 'Not unique'], 
        style       : ['','','','warning']
      },  
      'character_count' : { 
        label       : 'Text Count',
        description :  'Number of characters in the text content of this tag'
      },
      'class_name'  : { 
        label       : 'class',
        description :  'Value of the HTML class attribute'
      },
      'role'        : { 
        label       : 'role',
        description :  'Can be used to redefine the role of an element into a landmark or widget'
      },
      'alt'         : { 
        label       : 'alt',
        description :  'Value of the HTML alt attribute'
      },
      'alt_for_comparison' : { 
        label       : 'Normlized Alt',
        description : 'Normalized version of the alt text content used for comparison'
      },
      'has_alt' : { 
        label       : 'Alt Defined',
        description : 'True if the alt attribute was defined in markup'
      },
      'alt_length' : { 
        label       : 'Alt text length',
        description : 'The length of the text in the alt text attribute'
      },
      'title'       : { 
        label       : 'title',
        description : 'Value of the HTML title attribute'
      },
      'aria_describedby' : { 
        label       : 'aria-describedby',
        description : 'aria-describedby can be used to provide additional information about an element to AT users'
      },
      'aria_hidden' : { 
        label       : 'aria-hidden',
        description :  'aria-hidden can be used to hide information from assistive technologies that is visible graphically'
      },
      'aria_label'  : { 
        label       : 'aria-label',
        description :  'aria-label can be used to label form controls and widgets'
      },
      'aria_labelledby' : { 
        label       : 'aria-labelledby',
        description :  'aria-labelledby can be used to label form controls and widgets'
      },
      'xpath'       : { 
        label       : 'XPath',
        description : 'XPath information used for identifying the location of the element in the DOM'
      },
      'has_aria_describedby' : { 
        label       : 'Description',
        description : 'Description defined using the aria-describedby attribute'
      },
      'calculated_aria_description' : { 
        label       : 'Calculated Description',
        description : 'Calculated text content of a description defined using the aria-describedby attribute'
      },
      'for_id'  : { 
        label       : 'for',
        description : 'Value of the for attribute of a label element'
      },
      'parent_landmark_role'  : { 
        label       : 'Parent landmark role',
        description : 'Role of the landmark that contains this content'
      },
      'parent_landmark'  : { 
        label       : 'Containing landmark element',
        description : 'Landmark element that contains this content'
      },
      'is_widget'  : { 
        label       : 'ARIA widget role',
        description : 'If element is part of an aria widget'
      },
      'is_landmark'  : { 
        label       : 'ARIA landmark role',
        description : 'If element is part of an aria widget'
      },


    /*
     * Calculated values based on CSS properties
     */

      'graphical' : {
        label       : 'Graphical Visibility',
        description : 'Can the item be seen visually',
        values      : ['Undefined value', 'Unknown', 'Hidden', 'Visible']
      }, 
      'is_large_font' : { 
        label       : 'Large Font',
        description : 'Boolean value used in WCAG 2.0 evaluation of color contrast ratio'
      }, 
      'is_visible_to_at' : { 
        label       : 'AT Visible',
        description : 'Is the element and its content visible to Assistive Technology',
        values : ['undefined', 'unknown', 'hidden', 'visible']
      }, 
      'is_visible_onscreen' : { 
        label       : 'Screen Visible',
        description : 'Is the element and its content visible on screen',
        values : ['undefined', 'unknown', 'hidden', 'visible']
      }, 


    /*
     * Run time CSS style properties
     */

      'display'              : {
        label       : 'display',
        description :  'The value of the CSS display property'
      }, 
      'visibility'           : {
        label       : 'visibility',
        description :  'The value of the CSS visibility property'
      },        
      'color'                : {
        label       : 'color',
        description :  'The value of the CSS color property'
      },               
      'background_color' : {
        label       : 'background-color',
        description :  'The value of the CSS background-color property'
      },
      'background_image' : {
        label       : 'background-image',
        description :  'The value of the CSS background-image property'
      },
      'font_family'          : {
        label       : 'font-family',
        description :  'The value of the CSS font-family property'
      },
      'font_size'            : {
        label       : 'font-size',
        description :  'The value of the CSS font-size property'
      },
      'font_weight'          : {
        label       : 'font-weight',
        description :  'The value of the CSS font-weight property'
      },
      'position'             : {
        label       : 'position',
        description :  'The value of the CSS position property'
      },
      'left'                 :  {
        label       : 'left',
        description :  'The value of the CSS left property'
      },
      'top'                  : {
        label       : 'top',
        description :  'The value of the CSS top property'
      },

    /*
     * Abbreviation Cache object properties
     */
      'abbreviation_text' : {
        label       : 'Abbreviation',
        description :  'The text content of an ABBR or ACROYMN element'
      },

    /*
     * Image Cache object attributes
     */
      'source' : {
        label       : 'src',
        description : 'Value of the src attribute'
      },


    /*
     * Control Cache object attributes
     */
      'computed_label' : {
        label       : 'Label (computed)',
        description : 'The label communicated to assistive technologies for identifying the form control.'
      },     
      'computed_label_source' : {
        label       : 'Labeling Technique',
        description : 'The technique for defining the label',
        values      : ['unkown', 'none', 'label by reference', 'label encapsulation', 'title attribute', 'value attribute', 'alt attribute', 'button type', 'child text content', 'aria labelledby', 'aria label']
      },     
      'num_main_landmarks' : {
        label       : 'Main landmarks',
        description : 'Number of main landmarks'
      },     
      'num_visible_main_landmarks' : {
        label       : 'Visibile main landmarks',
        description : 'Number of visible main landmarks'
      },     
      'fieldset_element' : {
        label       : 'Fieldset/Legend',
        description : 'Content of the fieldset legend element'
      },     
      'legend_count' : {
        label       : 'Legend Count',
        description : 'Number of legend elements contained in a fieldset element'
      },     
      'accessible_name' : {
        label       : 'Name',
        description : 'The name of a widget used by assistive technologies to identify the widget.'
      },     
      'accessible_name_for_comparison' : {
        label       : 'Name (normalized)',
        description : 'Normailzed name of a widget for use in comparisons.'
      },     
      'accessible_description' : {
        label       : 'Description',
        description : 'The description used by assistive technologies to provide additional information about a form control, widget, link, image or other element.'
      },     
      'accessible_description_for_comparison' : {
        label       : 'Description (normalized)',
        description : 'Normalized description used for comparisons.'
      },     
    /*
     * Link Cache object attributes
     */
      'is_url' : {
        label       : 'is a url',
        description : 'Boolean value indicating if a href contains a URL'
      },
      'is_target' : {
        label       : 'is a target',
        description : 'Boolean value indicating if the link can be a target'
      },
      'link_type' : {
        label       : 'Type of link',
        description : 'Type of link',
        values      : ['empty', 'other', 'internal link', 'link', 'secure link', 'ftp', 'secure ftps', 'file', 'javascript', 'mail to', 'target only']
      },
      'accessible_name_source' : {
        label       : 'Name from',
        description : 'The technique for defining the accessible name of a link',
        values      : ['unkown', 'none', 'label by reference', 'label encapsulation', 'title attribute', 'value attribute', 'alt attribute', 'button type', 'child text content', 'aria labelledby', 'aria label']
      },     
      'is_broken' : {
        label       : 'Link broken',
        description : 'Tests to see if the link is broken, valid or has an error',
        values      : ['unkown',  'broken', 'vaild', 'not tested', 'error']
      },
      'name_attribute' : {
        label       : 'Name attribute',
        description : 'Value of the name attribute'
      },
      'target' : {
        label       : 'target',
        description : 'Value of the target attribute'
      },

    /*
     * Media Cache object properties
     */

      'is_live'  : {
        label       : 'Live',
        description :  'Is the media live video or audio',
        values      :  ['undefined', 'No', 'Maybe, manual verification required', 'Yes']
        
      },

      'is_video'  : {
        label       : 'Video',
        description :  'Does the media object contain video',
        values      :  ['undefined', 'No', 'Maybe, manual verification required', 'Yes']
        
      },

      'is_audio'  : {
        label       : 'Audio',
        description :  'Does the media object contain audio',
        values      :  ['undefined', 'No', 'Maybe, manual verification required', 'Yes']
      },

      'has_caption'  : {
        label       : 'Caption',
        description :  'Does the media object have captions',
        values      :  ['undefined', 'No', 'Maybe, manual verification required', 'Yes']
      },

      'has_text_alternative' : {
        label       : 'Text Equivalent',
        description :  'Does the media object have a text equivalent',
        values      :  ['undefined', 'No', 'Maybe, manual verification required', 'Yes']
      },
      
      'has_audio_description' : {
        label       : 'Audio Equivalent',
        description :  'Does the media object have a audio equivalent',
        values      :  ['undefined', 'No', 'Maybe, manual verification required', 'Yes']
      },
      
    /*
     * Name Cache object properties
     */

      'name'  : {
        label       : 'Name',
        description : 'Text content of the element'
      },
      'name_for_comparison' : {
        label       : 'Normalized name',
        description : 'Text content of the element normalized for use in comparisons'
      },
      'name_from_text_nodes' : {
        label       : 'Name from text',
        description : 'Text content of the element that comes from text dom nodes'
      },
      'name_from_image_alt' : {
        label       : 'Name from alt',
        description : 'Text content of the element that comes from alt text of images'
      },
      'image_count' : {
        label       : 'Image count',
        description : 'Number of images contained in the element'
      },
      'text_only_from_image' : {
        label       : 'Image only',
        description : 'Does the text content only come from images'
      },

    /*
     * List Cache object properties
     */

      'list_type'  : {
        label       : 'List Type',
        values      : ['Undefined', 'Container element', 'Item element', 'Landmark element'],        
        description : 'Type of list cache element'
      },
      
    /*
     * Table Cache object properties
     */

      'row'                      : {
        label       : 'Row',
        description :  'The row of the cell in a table'
      },
      'column'                   : {
        label       : 'Column',
        description :  'The column of the cell in a table'
      },
      'cell_count'                   : {
        label       : 'Cell Count',
        description :  'Number of TH and TD elements in the table'
      },
      'max_row'                      : {
        label       : 'Rows',
        description :  'Number of rows in a table'
      },
      'max_column'                   : {
        label       : 'Columns',
        description :  'Number of columns in a table'
      },
      'number_of_header_ids' : {
        label       : 'Header ID Num',
        description :  'Number of ids in a headers attribute'
      },              
      'text_content'             : {
        label       : 'Text',
        description :  'Text content of a table cell'
      },              
      'header_content'             : {
        label       : 'Header',
        description :  'Text content of header cells associated with the data cell'
      },
      'header_source'  : {
        label       : 'Header Source',
        values      : ["Undefined", "none", "headers attribute", "Column/Row header cells"],        
        description : 'How header cells were calculated for the table'
      },              
      'text_normalized'    : {
        label       : 'Text',
        description : 'Text content of a element'
      },              
      'effective_caption'        : {
        label       : 'Effective Caption',
        description : 'Effective caption is the text content of a caption element or ARIA labeling'
      },
      'effective_summary'        : {
        label       : 'Effective Summary',
        description : 'Effective summary is the text content of a summary attribute or an aria-describedby attribute'
      },
      'is_data_table'            : {
        label       : 'Data Table',
        description :  'True if a table has been identified as a data table, otherwise false'
      },
      'is_complex_data_table'            : {
        label       : 'Complex Data Table',
        description :  'True if a table has been identified as a complex data table, otherwise false'
      },
      'first_row_th_count'       : {
        label       : 'Header Count',
        description :  'The number of header cells in the first row or column of a data table'
      },
      'first_row_cell_count' : {
        label       : 'Cell Count',
        description :  'The number of none empty cells in the first row or column of a data table'
      },     
      'scope'               : {
        label       : 'scope',
        description :  'The value of the scope attribute of a table cell'
      },
      'headers'             : {
        label       : 'headers',
        description :  'The value of the headers attribute of a table cell'
      },
      'row_span'            : {
        label       : 'rowspan',
        description :  'The value of the rowspan attribute of a table cell'
      },
      'column_span'         : {
        label       : 'colspan',
        description :  'The value of the colspan attribute of a table cell'
      },
      'nesting_level'         : {
        label       : 'Nesting',
        description :  'The level of nesting of a layout table in other tables that are wider than 1 column and are not data tables'
      },
      'table_type'  : {
        label       : 'Table Type',
        values      : ["Undefined", "Table","Caption","Table Head","Table Body", "Row", "Header Cell", "Data Cell"],        
        description : 'Type of table element'
      },
      'table_role'  : {
        label       : 'Table Role',
        values      : ["Undefined", "Unknown","Layout","Data"],        
        description : 'Identifies if the table is being used for layout or tabular data on the page'
      }
      
    }
  }
);
/**
 * Copyright 2011 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* --------------------------------------------------------------------------- */
/* OpenAjax Alliance WCAG 2.0 National Language Support (NLS): English         */
/* --------------------------------------------------------------------------- */

OpenAjax.a11y.all_wcag20_nls.addNLS('en-us', {
  abbreviation : 'WCAG 2.0',
  title        : 'Web Content Accessibility Guidelines 2.0',
  url          : 'http://www.w3c.org/TR/WCAG20',

  level : "Level ",

  levels : ['Undefined', 'AAA','AA','', 'A'],

  evaluation_levels : ['Undefined', 'AAA','AA','AA and AAA', 'A',  'A and AAA', 'A nd AA', 'A, AA and AAA'],

  principles : {
    //
    // Principle 1: Perceivable
    //
    '1' : {
      id          : OpenAjax.a11y.WCAG20_PRINCIPLE.P_1,
      title       : 'Principle 1: Perceivable',
      description : 'Information and user interface components must be presentable to users in ways they can perceive.', 
      url_spec    : 'http://www.w3.org/TR/WCAG20/#perceivable',
      guidelines : {
        //
        // Guideline 1.1 Text Alternatives
        //
        '1.1' : {
          id          : OpenAjax.a11y.WCAG20_GUIDELINE.G_1_1,  
          title       : 'Guideline 1.1 Text Alternatives',
          description : 'Provide text alternatives for any non-text content so that it can be changed into other forms people need, such as large print, braille, speech, symbols or simpler language.', 
          url_spec    : 'http://www.w3.org/TR/WCAG20/#text-equiv',
          success_criteria : {
            //
            // Success Criterion 1.1.1 Non-text Content: All non-text content that is presented to the user has a text alternative that serves the equivalent purpose, except for the situations listed below. 
            //
            '1.1.1' : {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_1_1_1,          
              level          : OpenAjax.a11y.WCAG20_LEVEL.A,
              title          : '1.1.1 Non-text Content',
              description    : 'All non-text content that is presented to the user has a text alternative that serves the equivalent purpose, except for the situations listed below.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#text-equiv',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-text-equiv-all',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/text-equiv-all.html',
              references     : []
            }
          }
        },  
        //
        // Guideline 1.2 Time-based Media
        //
        '1.2' : {
          id          : OpenAjax.a11y.WCAG20_GUIDELINE.G_1_2,  
          title       : 'Guideline 1.2 Time-based Media',
          description : 'Provide alternatives for time-based media.', 
          url_spec    : 'http://www.w3.org/TR/WCAG20/#media-equiv',
          success_criteria : {
            //
            // Success Criterion 1.2.1 Audio-only and Video-only (Prerecorded)
            //
            '1.2.1': {
              id          : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_1_2_1,          
              level       : OpenAjax.a11y.WCAG20_LEVEL.A,
              title       : '1.2.1 Audio-only and Video-only (Prerecorded)',
              description : 'For prerecorded audio-only and prerecorded video-only media, the following are true, except when the audio or video is a media alternative for text and is clearly labeled as such: (1) Prerecorded Audio-only: An alternative for time-based media is provided that presents equivalent information for prerecorded audio-only content. (2) Prerecorded Video-only: Either an alternative for time-based media or an audio track is provided that presents equivalent information for prerecorded video-only content.',
              url_spec    : 'http://www.w3.org/TR/WCAG20/#media-equiv-av-only-alt',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-media-equiv-av-only-alt',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/media-equiv-av-only-alt.html',
              references     : []
            },
            //
            // Success Criterion 1.2.2 Captions (Prerecorded)
            //
           '1.2.2': {
              id          : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_1_2_2,          
              level       : OpenAjax.a11y.WCAG20_LEVEL.A,
              title       : '1.2.2 Captions (Prerecorded)',
              description : 'Captions are provided for all prerecorded audio content in synchronized media, except when the media is a media alternative for text and is clearly labeled as such.',
              url_spec    : 'http://www.w3.org/TR/WCAG20/#media-equiv-captions',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-media-equiv-captions',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/media-equiv-captions.html',
              references     : []
            },
            //
            // Success Criteria 1.2.3 Audio Description or Media Alternative (Prerecorded)
            //
            '1.2.3': {
              id          : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_1_2_3,          
              level       : OpenAjax.a11y.WCAG20_LEVEL.A,
              title       : '1.2.3 Audio Description or Media Alternative (Prerecorded)',
              description : 'An alternative for time-based media or audio description of the prerecorded video content is provided for synchronized media, except when the media is a media alternative for text and is clearly labeled as such.',
              url_spec    : 'http://www.w3.org/TR/WCAG20/#media-equiv-audio-desc',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-media-equiv-audio-desc',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/media-equiv-audio-desc.html',
              references     : []
            },
            //
            // Success Criteria 1.2.4 Captions (Live)
            //
            '1.2.4': {
              id          : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_1_2_4,          
              level       : OpenAjax.a11y.WCAG20_LEVEL.AA,
              title       : '1.2.4 Captions (Live)',
              description : 'Captions are provided for all live audio content in synchronized media. ',
              url_spec    : 'http://www.w3.org/TR/WCAG20/#media-equiv-real-time-captions',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-media-equiv-real-time-captions',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/media-equiv-real-time-captions.html',
              references     : []
            },
            //
            // Success Criteria 1.2.5 Audio Description (Prerecorded)
            //
            '1.2.5': {
              id          : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_1_2_5,          
              level       : OpenAjax.a11y.WCAG20_LEVEL.AA,
              title       : '1.2.5 Audio Description (Prerecorded)',
              description : 'Audio description is provided for all prerecorded video content in synchronized media.',
              url_spec    : 'http://www.w3.org/TR/WCAG20/#media-equiv-audio-desc-only',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-media-equiv-audio-desc-only',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/media-equiv-audio-desc-only.html',
              references     : []
            },
            //
            // Success Criteria 1.2.6 Sign Language (Prerecorded)
            //
            '1.2.6': {
              id          : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_1_2_6,          
              level       : OpenAjax.a11y.WCAG20_LEVEL.AAA,
              title       : '1.2.6 Sign Language (Prerecorded)',
              description : 'Sign language interpretation is provided for all prerecorded audio content in synchronized media.',
              url_spec    : 'http://www.w3.org/TR/WCAG20/#media-equiv-sign',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-media-equiv-sign',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/media-equiv-sign.html',
              references     : []
            },
            //
            // Success Criteria 1.2.7 Extended Audio Description (Prerecorded)
            //
            '1.2.7': {
              id          : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_1_2_7,          
              level       : OpenAjax.a11y.WCAG20_LEVEL.AAA,
              title       : '1.2.7 Extended Audio Description (Prerecorded)',
              description : 'Where pauses in foreground audio are insufficient to allow audio descriptions to convey the sense of the video, extended audio description is provided for all prerecorded video content in synchronized media.',
              url_spec    : 'http://www.w3.org/TR/WCAG20/#media-equiv-extended-ad',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-media-equiv-extended-ad',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/media-equiv-extended-ad.html',
              references     : []
            },
            //
            // Success Criteria 1.2.8 Media Alternative (Prerecorded)
            //
            '1.2.8': {
              id          : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_1_2_8,          
              level       : OpenAjax.a11y.WCAG20_LEVEL.AAA,
              title       : '1.2.8 Media Alternative (Prerecorded)',
              description : 'An alternative for time-based media is provided for all prerecorded synchronized media and for all prerecorded video-only media.',
              url_spec    : 'http://www.w3.org/TR/WCAG20/#media-equiv-text-doc',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-media-equiv-text-doc',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/media-equiv-text-doc.html',
              references     : []
            },
            //
            // Success Criteria 1.2.9 Audio-only (Live)
            //
            '1.2.9': {
              id          : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_1_2_9,          
              level       : OpenAjax.a11y.WCAG20_LEVEL.AAA,
              title       : '1.2.9 Audio-only (Live)',
              description : 'An alternative for time-based media that presents equivalent information for live audio-only content is provided. ',
              url_spec    : 'http://www.w3.org/TR/WCAG20/#media-equiv-live-audio-only',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-media-equiv-live-audio-only',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/media-equiv-live-audio-only.html',
              references     : []
            }
          }
        },
        //
        // Guideline 1.3 Adaptable
        //
        '1.3' : {
          id          : OpenAjax.a11y.WCAG20_GUIDELINE.G_1_3,  
          title       : 'Guideline 1.3 Adaptable',
          description : 'Create content that can be presented in different ways (for example simpler layout) without losing information or structure.', 
          url_spec    : 'http://www.w3.org/TR/WCAG20/#content-structure-separation',
          success_criteria : {
            //
            // Success Criteria 1.3.1 Info and Relationships
            //
            '1.3.1': {
              id          : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_1_3_1,          
              level       : OpenAjax.a11y.WCAG20_LEVEL.A,
              title       : '1.3.1 Info and Relationships',
              description : 'Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text.',
              url_spec    : 'http://www.w3.org/TR/WCAG20/#content-structure-separation-programmatic',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-content-structure-separation-programmatic',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/content-structure-separation-programmatic.html',
              references     : []
            },
            //
            // Success Criteria 1.3.2 Meaningful Sequence
            //
            '1.3.2': {
              id          : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_1_3_2,
              level       : OpenAjax.a11y.WCAG20_LEVEL.A,
              title       : '1.3.2 Meaningful Sequence',
              description : 'When the sequence in which content is presented affects its meaning, a correct reading sequence can be programmatically determined.',
              url_spec    : 'http://www.w3.org/TR/WCAG20/#content-structure-separation-sequence',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-content-structure-separation-sequence',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/content-structure-separation-sequence.html',
              references     : []
            },
            //
            // Success Criteria 1.3.3 Sensory Characteristics
            //
            '1.3.3': {
              id          : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_1_3_3,
              level       : OpenAjax.a11y.WCAG20_LEVEL.A,
              title       : '1.3.3 Sensory Characteristics',
              description : 'Instructions provided for understanding and operating content do not rely solely on sensory characteristics of components such as shape, size, visual location, orientation, or sound.',
              url_spec    : 'http://www.w3.org/TR/WCAG20/#content-structure-separation-understanding',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-content-structure-separation-understanding',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/content-structure-separation-understanding.html',
              references     : []
            }
          }
        },
        //
        // Guideline 1.4 Distinguishable
        //
        '1.4' : {
          id          : OpenAjax.a11y.WCAG20_GUIDELINE.G_1_4,          
          title       : 'Guideline 1.4 Distinguishable',
          description : 'Make it easier for users to see and hear content including separating foreground from background.', 
          url_spec    : 'http://www.w3.org/TR/WCAG20/#visual-audio-contrast',
          success_criteria : {
            //
            // Success Criteria 1.4.1 Use of Color
            //
            '1.4.1': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_1_4_1,
              level          : OpenAjax.a11y.WCAG20_LEVEL.A,
              title          : '1.4.1 Use of Color',
              description    : 'Color is not used as the only visual means of conveying information, indicating an action, prompting a response, or distinguishing a visual element.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#visual-audio-contrast-without-color',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-visual-audio-contrast-without-color',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-without-color.html',
              references     : []
            },
            //
            // Success Criteria 1.4.2 Audio Control
            //
            '1.4.2': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_1_4_2,
              level          : OpenAjax.a11y.WCAG20_LEVEL.A,
              title          : '1.4.2 Audio Control',
              description    : 'If any audio on a Web page plays automatically for more than 3 seconds, either a mechanism is available to pause or stop the audio, or a mechanism is available to control audio volume independently from the overall system volume level.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#visual-audio-contrast-dis-audio',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-visual-audio-contrast-dis-audio',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-dis-audio.html',
              references     : []
            },
            //
            // Success Criteria 1.4.3 Contrast (Minimum)
            //
            '1.4.3': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_1_4_3,
              level          : OpenAjax.a11y.WCAG20_LEVEL.AA,
              title          : '1.4.3 Contrast (Minimum)',
              description    : 'The visual presentation of text and images of text has a contrast ratio of at least 4.5:1, except for the following: \n(1) Large Text: Large-scale text and images of large-scale text have a contrast ratio of at least 3:1;\n(2) Incidental: Text or images of text that are part of an inactive user interface component, that are pure decoration, that are not visible to anyone, or that are part of a picture that contains significant other visual content, have no contrast requirement.\n(3) Logotypes: Text that is part of a logo or brand name has no minimum contrast requirement.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#visual-audio-contrast-contrast',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-visual-audio-contrast-contrast',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html',
              references     : []
            },
            //
            // Success Criteria 1.4.4 Resize text
            //
            '1.4.4': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_1_4_4,
              level          : OpenAjax.a11y.WCAG20_LEVEL.AA,
              title          : '1.4.4 Resize text',
              description    : 'Except for captions and images of text, text can be resized without assistive technology up to 200 percent without loss of content or functionality.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#visual-audio-contrast-scale',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-visual-audio-contrast-scale',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-scale.html',
              references     : []
            },
            //
            // Success Criteria 1.4.5 Images of Text
            //
            '1.4.5': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_1_4_5,
              level          : OpenAjax.a11y.WCAG20_LEVEL.AA,
              title          : '1.4.5 Images of Text',
              description    : 'If the technologies being used can achieve the visual presentation, text is used to convey information rather than images of text except for the following: (1) Customizable: The image of text can be visually customized to the user\'s requirements; (2) Essential: A particular presentation of text is essential to the information being conveyed.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#visual-audio-contrast-text-presentation',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-visual-audio-contrast-text-presentation',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-text-presentation.html',
              references     : []
            },
            //
            // Success Criteria 1.4.6 Contrast (Enhanced)
            //
            '1.4.6': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_1_4_6,
              level          : OpenAjax.a11y.WCAG20_LEVEL.AAA,
              title          : '1.4.6 Contrast (Enhanced)',
              description    : 'The visual presentation of text and images of text has a contrast ratio of at least 7:1, except for the following: (1) Large Text: Large-scale text and images of large-scale text have a contrast ratio of at least 4.5:1; (2) Incidental: Text or images of text that are part of an inactive user interface component, that are pure decoration, that are not visible to anyone, or that are part of a picture that contains significant other visual content, have no contrast requirement. (3) Logotypes: Text that is part of a logo or brand name has no minimum contrast requirement.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#visual-audio-contrast7',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-visual-audio-contrast7',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast7.html',
              references     : []
            },
            //
            // Success Criteria 1.4.7 Low or No Background Audio
            //
            '1.4.7': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_1_4_7,
              level          : OpenAjax.a11y.WCAG20_LEVEL.AAA,
              title          : '1.4.7 Low or No Background Audio',
              description    : 'For prerecorded audio-only content that (1) contains primarily speech in the foreground, (2) is not an audio CAPTCHA or audio logo, and (3) is not vocalization intended to be primarily musical expression such as singing or rapping, at least one of the following is true: (4a) No Background: The audio does not contain background sounds. (4b) Turn Off: The background sounds can be turned off. (4c) 20 dB: The background sounds are at least 20 decibels lower than the foreground speech content, with the exception of occasional sounds that last for only one or two seconds.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#visual-audio-contrast-noaudio',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-visual-audio-contrast-noaudio',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-noaudio.html',
              references     : []
            },
            //
            // Success Criteria 1.4.8 Visual Presentation
            //
            '1.4.8': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_1_4_8,
              level          : OpenAjax.a11y.WCAG20_LEVEL.AAA,
              title          : '1.4.8 Visual Presentation',
              description    : 'For the visual presentation of blocks of text, a mechanism is available to achieve the following: (1) Foreground and background colors can be selected by the user; (2) Width is no more than 80 characters or glyphs (40 if CJK); (3) Text is not justified (aligned to both the left and the right margins); (4) Line spacing (leading) is at least space-and-a-half within paragraphs, and paragraph spacing is at least 1.5 times larger than the line spacing; (5) Text can be resized without assistive technology up to 200 percent in a way that does not require the user to scroll horizontally to read a line of text on a full-screen window.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#visual-audio-contrast-visual-presentation',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-visual-audio-contrast-visual-presentation',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-visual-presentation.html',
              references     : []
            },
            //
            // Success Criteria 1.4.9 Images of Text (No Exception)
            //
            '1.4.9': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_1_4_9,
              level          : OpenAjax.a11y.WCAG20_LEVEL.AAA,
              title          : '1.4.9 Images of Text (No Exception)',
              description    : 'Images of text are only used for pure decoration or where a particular presentation of text is essential to the information being conveyed.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#visual-audio-contrast-text-images',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-visual-audio-contrast-text-images',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-text-images.html',
              references     : []
            }
          }
        }
      }
    },  
    //
    // Principle 2: Operable
    //
    '2' : {
      id          : OpenAjax.a11y.WCAG20_PRINCIPLE.P_2,
      title       : 'Principle 2: Operable',
      description : 'User interface components and navigation must be operable.', 
      url_spec    : 'http://www.w3.org/TR/WCAG20/#operable',
      guidelines : {
        //
        // Guideline 2.1 Keyboard Accessible
        //
        '2.1' : {
          id          : OpenAjax.a11y.WCAG20_GUIDELINE.G_2_1,  
          title       : 'Guideline 2.1 Keyboard Accessible',
          description : 'Make all functionality available from a keyboard.', 
          url_spec    : 'http://www.w3.org/TR/WCAG20/#keyboard-operation',
          success_criteria : {
            //
            // Success Criterion 2.1.1 Keyboard
            //
            '2.1.1': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_2_1_1,
              level          : OpenAjax.a11y.WCAG20_LEVEL.A,
              title          : '2.1.1 Keyboard',
              description    : 'All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes, except where the underlying function requires input that depends on the path of the user\'s movement and not just the endpoints.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#keyboard-operation-keyboard-operable',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-keyboard-operation-keyboard-operable',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/keyboard-operation-keyboard-operable.html',
              references     : []
            },
            //
            // Success Criterion 2.1.2 No Keyboard Trap
            //
            '2.1.2': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_2_1_2,
              level          : OpenAjax.a11y.WCAG20_LEVEL.A,
              title          : '2.1.2 No Keyboard Trap',
              description    : 'If keyboard focus can be moved to a component of the page using a keyboard interface, then focus can be moved away from that component using only a keyboard interface, and, if it requires more than unmodified arrow or tab keys or other standard exit methods, the user is advised of the method for moving focus away.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#keyboard-operation-trapping',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-keyboard-operation-trapping',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/keyboard-operation-trapping.html',
              references     : []
            },
            //
            // Success Criterion 2.1.3 Keyboard (No Exception)
            //
            '2.1.3': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_2_1_3,
              level          : OpenAjax.a11y.WCAG20_LEVEL.AAA,
              title          : '2.1.3 Keyboard (No Exception)',
              description    : 'All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#keyboard-operation-all-funcs',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-keyboard-operation-all-funcs',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/keyboard-operation-all-funcs.html',
              references     : []
            }
          }
        },
        //
        // Guideline 2.2 Enough Time
        //
        '2.2' : {
          id          : OpenAjax.a11y.WCAG20_GUIDELINE.G_2_2,  
          title       : 'Guideline 2.2 Enough Time',
          description : 'Provide users enough time to read and use content.', 
          url_spec    : 'http://www.w3.org/TR/WCAG20/#time-limits',
          success_criteria : {
            //
            // Success Criterion 2.2.1 Timing Adjustable
            //
            '2.2.1': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_2_2_1,
              level          : OpenAjax.a11y.WCAG20_LEVEL.A,
              title          : '2.2.1 Timing Adjustable',
              description    : 'For each time limit that is set by the content, at least one of the following is true: (1) Turn off: The user is allowed to turn off the time limit before encountering it; or (2) Adjust: The user is allowed to adjust the time limit before encountering it over a wide range that is at least ten times the length of the default setting; or (3) Extend: The user is warned before time expires and given at least 20 seconds to extend the time limit with a simple action (for example, "press the space bar"), and the user is allowed to extend the time limit at least ten times; or (4) Real-time Exception: The time limit is a required part of a real-time event (for example, an auction), and no alternative to the time limit is possible; or (5) Essential Exception: The time limit is essential and extending it would invalidate the activity; or (6) 20 Hour Exception: The time limit is longer than 20 hours.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#time-limits-required-behaviors',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-time-limits-required-behaviors',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/time-limits-required-behaviors.html',
              references     : []
            },
            //
            // Success Criteria 2.2.2 Pause, Stop, Hide
            //
            '2.2.2': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_2_2_2,
              level          : OpenAjax.a11y.WCAG20_LEVEL.A,
              title          : '2.2.2 Pause, Stop, Hide',
              description    : 'For moving, blinking, scrolling, or auto-updating information, all of the following are true: Moving, blinking, scrolling: For any moving, blinking or scrolling information that (1) starts automatically, (2) lasts more than five seconds, and (3) is presented in parallel with other content, there is a mechanism for the user to pause, stop, or hide it unless the movement, blinking, or scrolling is part of an activity where it is essential; and Auto-updating: For any auto-updating information that (1) starts automatically and (2) is presented in parallel with other content, there is a mechanism for the user to pause, stop, or hide it or to control the frequency of the update unless the auto-updating is part of an activity where it is essential.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#time-limits-pause',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-time-limits-pause',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/time-limits-pause.html',
              references     : []
            },
            //
            // Success Criteria 2.2.3 No Timing
            //
            '2.2.3': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_2_2_3,
              level          : OpenAjax.a11y.WCAG20_LEVEL.AAA,
              title          : '2.2.3 No Timing',
              description    : 'Timing is not an essential part of the event or activity presented by the content, except for non-interactive synchronized media and real-time events.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#time-limits-no-exceptions',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-time-limits-no-exceptions',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/time-limits-no-exceptions.html',
              references     : []
            },
            //
            // Success Criteria 2.2.4 Interruptions
            //
            '2.2.4': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_2_2_4,
              level          : OpenAjax.a11y.WCAG20_LEVEL.AAA,
              title          : '2.2.4 Interruptions',
              description    : 'Interruptions can be postponed or suppressed by the user, except interruptions involving an emergency.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#time-limits-postponed',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-time-limits-postponed',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/time-limits-postponed.html',
              references     : []
            },
            //
            // Success Criteria 2.2.5 Re-authenticating
            //
            '2.2.5': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_2_2_5,
              level          : OpenAjax.a11y.WCAG20_LEVEL.AAA,
              title          : '2.2.5 Re-authenticating',
              description    : 'When an authenticated session expires, the user can continue the activity without loss of data after re-authenticating.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#time-limits-server-timeout',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-time-limits-server-timeout',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/time-limits-server-timeout.html',
              references     : []
            }          
          }
        },
        //
        // Guideline 2.3 Seizures
        //
        '2.3' : {
          id          : OpenAjax.a11y.WCAG20_GUIDELINE.G_2_3,  
          title       : 'Guideline 2.3 Seizures',
          description : 'Do not design content in a way that is known to cause seizures.', 
          url_spec    : 'http://www.w3.org/TR/WCAG20/#seizure',
          success_criteria : {
            //
            // Success Criteria 2.3.1 Three Flashes or Below Threshold
            //
            '2.3.1': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_2_3_1,
              level          : OpenAjax.a11y.WCAG20_LEVEL.A,
              title          : '2.3.1 Three Flashes or Below Threshold',
              description    : 'Web pages do not contain anything that flashes more than three times in any one second period, or the flash is below the general flash and red flash thresholds.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#seizure-does-not-violate',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-seizure-does-not-violate',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/seizure-does-not-violate.html',
              references     : []
            },
            //
            // Success Criteria 2.3.2 Three Flashes
            //
            '2.3.2': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_2_3_2,
              level          : OpenAjax.a11y.WCAG20_LEVEL.AAA,
              title          : '2.3.2 Three Flashes',
              description    : 'Web pages do not contain anything that flashes more than three times in any one second period.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#seizure-three-times',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-seizure-three-times',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/seizure-three-times.html',
              references     : []
            }
          }
        },
        //
        // Guideline 2.4 Navigable
        //
        '2.4' : {
          id          : OpenAjax.a11y.WCAG20_GUIDELINE.G_2_4,  
          title       : 'Guideline 2.4 Navigable',
          description : 'Provide ways to help users navigate, find content, and determine where they are.', 
          url_spec    : 'http://www.w3.org/TR/WCAG20/#navigation-mechanisms',
          success_criteria : {
            //
            // Success Criteria 2.4.1 Bypass Blocks
            //
            '2.4.1': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_2_4_1,
              level          : OpenAjax.a11y.WCAG20_LEVEL.A,
              title          : '2.4.1 Bypass Blocks',
              description    : 'A mechanism is available to bypass blocks of content that are repeated on multiple Web pages.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#navigation-mechanisms-skip',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-navigation-mechanisms-skip',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/navigation-mechanisms-skip.html',
              references     : []
            },
            //
            // Success Criteria 2.4.2 Page Titled
            //
            '2.4.2': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_2_4_2,
              level          : OpenAjax.a11y.WCAG20_LEVEL.A,
              title          : '2.4.2 Page Titled',
              description    : 'Web pages have titles that describe topic or purpose.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#navigation-mechanisms-title',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-navigation-mechanisms-title',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/navigation-mechanisms-title.html',
              references     : []
            },
            //
            // Success Criteria 2.4.3 Focus Order
            //
            '2.4.3': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_2_4_3,
              level          : OpenAjax.a11y.WCAG20_LEVEL.A,
              title          : '2.4.3 Focus Order',
              description    : 'If a Web page can be navigated sequentially and the navigation sequences affect meaning or operation, focusable components receive focus in an order that preserves meaning and operability.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#navigation-mechanisms-focus-order',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-navigation-mechanisms-focus-order',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/navigation-mechanisms-focus-order.html',
              references     : []
            },
            //
            // Success Criteria 2.4.4 Link Purpose (In Context)
            //
            '2.4.4': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_2_4_4,
              level          : OpenAjax.a11y.WCAG20_LEVEL.AA,
              title          : '2.4.4 Link Purpose (In Context)',
              description    : 'The purpose of each link can be determined from the link text alone or from the link text together with its programmatically determined link context, except where the purpose of the link would be ambiguous to users in general.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#navigation-mechanisms-refs',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-navigation-mechanisms-refs',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/navigation-mechanisms-refs.html',
              references     : []
            },
            //
            // Success Criteria 2.4.5 Multiple Ways
            //
            '2.4.5': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_2_4_5,
              level          : OpenAjax.a11y.WCAG20_LEVEL.A,
              title          : '2.4.5 Multiple Ways',
              description    : 'More than one way is available to locate a Web page within a set of Web pages except where the Web Page is the result of, or a step in, a process.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#navigation-mechanisms-mult-loc',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-navigation-mechanisms-mult-loc',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/navigation-mechanisms-mult-loc.html',
              references     : []
            },
            //
            // Success Criteria 2.4.6 Headings and Labels
            //
            '2.4.6': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_2_4_6,
              level          : OpenAjax.a11y.WCAG20_LEVEL.AA,
              title          : '2.4.6 Headings and Labels',
              description    : 'Headings and labels describe topic or purpose.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#navigation-mechanisms-descriptive',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-navigation-mechanisms-descriptive',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/navigation-mechanisms-descriptive.html',
              references     : []
            },
            //
            // Success Criteria 2.4.7 Focus Visible
            //
            '2.4.7': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_2_4_7,
              level          : OpenAjax.a11y.WCAG20_LEVEL.AA,
              title          : '2.4.7 Focus Visible',
              description    : 'Any keyboard operable user interface has a mode of operation where the keyboard focus indicator is visible. ',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#navigation-mechanisms-focus-visible',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-navigation-mechanisms-focus-visible',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/navigation-mechanisms-focus-visible.html',
              references     : []
            },
            //
            // Success Criteria 2.4.8 Location
            //
            '2.4.8': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_2_4_8,
              level          : OpenAjax.a11y.WCAG20_LEVEL.AAA,
              title          : '2.4.8 Location',
              description    : 'Information about the user\'s location within a set of Web pages is available.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#navigation-mechanisms-location',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-navigation-mechanisms-location',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/navigation-mechanisms-location.html',
              references     : []
            },
            //
            // Success Criteria 2.4.9 Link Purpose (Link Only)
            //
            '2.4.9': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_2_4_9,
              level          : OpenAjax.a11y.WCAG20_LEVEL.AAA,
              title          : '2.4.9 Link Purpose (Link Only)',
              description    : 'A mechanism is available to allow the purpose of each link to be identified from link text alone, except where the purpose of the link would be ambiguous to users in general.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#navigation-mechanisms-link',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-navigation-mechanisms-link',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/navigation-mechanisms-link.html',
              references     : []
            },
            //
            // Success Criteria 2.4.10 Section Headings
            //
            '2.4.10': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_2_4_10,
              level          : OpenAjax.a11y.WCAG20_LEVEL.AAA,
              title          : '2.4.10 Section Headings',
              description    : 'Section headings are used to organize the content.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#navigation-mechanisms-headings',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-navigation-mechanisms-headings',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/navigation-mechanisms-headings.html',
              references     : []
            }
          }
        }
      }
    },  
    //
    // Principle 3: Understandable
    //
    '3' : {
      id          : OpenAjax.a11y.WCAG20_PRINCIPLE.P_3,
      title       : 'Principle 3: Understandable',
      description : 'Information and the operation of user interface must be understandable.', 
      url_spec    : 'http://www.w3.org/TR/WCAG20/#understandable',
      guidelines : {
        //
        // Guideline 3.1 Readable
        //
        '3.1' : {
          id          : OpenAjax.a11y.WCAG20_GUIDELINE.G_3_1,  
          title       : 'Guideline 3.1 Readable',
          description : 'Make text content readable and understandable.', 
          url_spec    : 'http://www.w3.org/TR/WCAG20/#meaning',
          success_criteria : {
            //
            // Success Criteria 3.1.1 Language of Page
            //
            '3.1.1': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_3_1_1,
              level          : OpenAjax.a11y.WCAG20_LEVEL.A,
              title          : '3.1.1 Language of Page',
              description    : 'The default human language  of each Web page  can be programmatically determined. ',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#meaning-doc-lang-id',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-meaning-doc-lang-id',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/meaning-doc-lang-id.html',
              references     : []
            },
            //
            // Success Criteria 3.1.2 Language of Parts
            //
            '3.1.2': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_3_1_2,
              level          : OpenAjax.a11y.WCAG20_LEVEL.AA,
              title          : '3.1.2 Language of Parts',
              description    : 'The human language of each passage or phrase in the content can be programmatically determined except for proper names, technical terms, words of indeterminate language, and words or phrases that have become part of the vernacular of the immediately surrounding text.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#meaning-other-lang-id',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-meaning-other-lang-id',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/meaning-other-lang-id.html',
              references     : []
            },
            //
            // Success Criteria 3.1.3 Unusual Words
            //
            '3.1.3': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_3_1_3,
              level          : OpenAjax.a11y.WCAG20_LEVEL.AAA,
              title          : '3.1.3 Unusual Words',
              description    : 'A mechanism is available for identifying specific definitions of words or phrases used in an unusual or restricted way, including idioms and jargon.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#meaning-idioms',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-meaning-idioms',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/meaning-idioms.html',
              references     : []
            },
            //
            // Success Criteria 3.1.4 Abbreviations
            //
            '3.1.4': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_3_1_4,
              level          : OpenAjax.a11y.WCAG20_LEVEL.AAA,
              title          : '3.1.4 Abbreviations',
              description    : 'A mechanism for identifying the expanded form or meaning of abbreviations is available. ',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#meaning-located',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-meaning-located',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/meaning-located.html',
              references     : []
            },
            //
            // Success Criteria 3.1.5 Reading Level
            //
            '3.1.5': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_3_1_5,
              level          : OpenAjax.a11y.WCAG20_LEVEL.AAA,
              title          : '3.1.5 Reading Level',
              description    : 'When text requires reading ability more advanced than the lower secondary education level after removal of proper names and titles, supplemental content, or a version that does not require reading ability more advanced than the lower secondary education level, is available. ',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#meaning-supplements',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-meaning-supplements',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/meaning-supplements.html',
              references     : []
            },
            //
            // Success Criteria 3.1.6 Pronunciation
            //
            '3.1.6': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_3_1_6,
              level          : OpenAjax.a11y.WCAG20_LEVEL.AAA,
              title          : '3.1.6 Pronunciation',
              description    : 'A mechanism is available for identifying specific pronunciation of words where meaning of the words, in context, is ambiguous without knowing the pronunciation.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#meaning-pronunciation',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-meaning-pronunciation',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/meaning-pronunciation.html',
              references     : []
            }
          }
        },
        //
        // Guideline 3.2 Predictable
        //
        '3.2' : {
          id          : OpenAjax.a11y.WCAG20_GUIDELINE.G_3_2,  
          title       : 'Guideline 3.2 Predictable',
          description : 'Make Web pages appear and operate in predictable ways.', 
          url_spec    : 'http://www.w3.org/TR/WCAG20/#consistent-behavior',
          success_criteria : {
            //
            // Success Criteria 3.2.1 On Focus
            //
            '3.2.1': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_3_2_1,
              level          : OpenAjax.a11y.WCAG20_LEVEL.A,
              title          : '3.2.1 On Focus',
              description    : 'When any component receives focus, it does not initiate a change of context.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#consistent-behavior-receive-focus',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-consistent-behavior-receive-focus',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/consistent-behavior-receive-focus.html',
              references     : []
            },
            //
            // Success Criteria 3.2.2 On Input
            //
            '3.2.2': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_3_2_2,
              level          : OpenAjax.a11y.WCAG20_LEVEL.A,
              title          : '3.2.2 On Input',
              description    : 'Changing the setting of any user interface component  does not automatically cause a change of context  unless the user has been advised of the behavior before using the component.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#consistent-behavior-unpredictable-change',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-consistent-behavior-unpredictable-change',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/consistent-behavior-unpredictable-change.html',
              references     : []
            },
            //
            // Success Criteria 3..2.3 Consistent Navigation
            //
            '3.2.3': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_3_2_3,
              level          : OpenAjax.a11y.WCAG20_LEVEL.AA,
              title          : '3.2.3 Consistent Navigation',
              description    : 'Navigational mechanisms that are repeated on multiple Web pages within a set of Web pages  occur in the same relative order each time they are repeated, unless a change is initiated by the user.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#consistent-behavior-consistent-locations',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-consistent-behavior-consistent-locations',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/consistent-behavior-consistent-locations.html',
              references     : []
            },
            //
            // Success Criteria 3.2.4 Consistent Identification
            //
            '3.2.4': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_3_2_4,
              level          : OpenAjax.a11y.WCAG20_LEVEL.AA,
              title          : '3.2.4 Consistent Identification',
              description    : 'Components that have the same functionality within a set of Web pages are identified consistently.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#consistent-behavior-consistent-functionality',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-consistent-behavior-consistent-functionality',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/consistent-behavior-consistent-functionality.html',
              references     : []
            },
            //
            // Success Criteria 3..2.5 Change on Request
            //
            '3.2.5': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_3_2_5,
              level          : OpenAjax.a11y.WCAG20_LEVEL.AAA,
              title          : '3.2.5 Change on Request',
              description    : 'Changes of context are initiated only by user request or a mechanism is available to turn off such changes.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#consistent-behavior-no-extreme-changes-context',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-consistent-behavior-no-extreme-changes-context',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/consistent-behavior-no-extreme-changes-context.html',
              references     : []
            }
          }
        },
        //
        // Guideline 3.3 Input Assistance
        //
        '3.3' : {
          id          : OpenAjax.a11y.WCAG20_GUIDELINE.G_3_3,  
          title       : 'Guideline 3.3 Input Assistance',
          description : 'Help users avoid and correct mistakes.', 
          url_spec    : 'http://www.w3.org/TR/WCAG20/#minimize-error',
          success_criteria : {
            //
            // Success Criteria 3.3.1 Error Identification
            //
            '3.3.1': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_3_3_1,
              level          : OpenAjax.a11y.WCAG20_LEVEL.A,
              title          : '3.3.1 Error Identification',
              description    : 'If an input error is automatically detected, the item that is in error is identified and the error is described to the user in text.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#minimize-error-identified',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-minimize-error-identified',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/minimize-error-identified.html',
              references     : []
            },
            //
            // Success Criteria 3.3.2 Labels or Instructions
            //
            '3.3.2': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_3_3_2,
              level          : OpenAjax.a11y.WCAG20_LEVEL.A,
              title          : '3.3.2 Labels or Instructions',
              description    : 'Labels or instructions are provided when content requires user input.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#minimize-error-cues',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-minimize-error-cues',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/minimize-error-cues.html',
              references     : []
            },
            //
            // Success Criteria 3.3.3 Error Suggestion
            //
            '3.3.3': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_3_3_3,
              level          : OpenAjax.a11y.WCAG20_LEVEL.AA,
              title          : '3.3.3 Error Suggestion',
              description    : 'If an input error is automatically detected and suggestions for correction are known, then the suggestions are provided to the user, unless it would jeopardize the security or purpose of the content.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#minimize-error-suggestions',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-minimize-error-suggestions',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/minimize-error-suggestions.html',
              references     : []
            }, 
            //
            // Success Criteria 3.3.4 Error Prevention (Legal, Financial, Data)
            //
            '3.3.4': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_3_3_4,
              level          : OpenAjax.a11y.WCAG20_LEVEL.AA,
              title          : '3.3.4 Error Prevention (Legal, Financial, Data)',
              description    : 'For Web pages that cause legal commitments or financial transactions for the user to occur, that modify or delete user-controllable data in data storage systems, or that submit user test responses, at least one of the following is true:',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#minimize-error-reversible',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-minimize-error-reversible',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/minimize-error-reversible.html',
              references     : []
            },
            //
            // Success Criteria 3.3.5 Help
            //
            '3.3.5': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_3_3_5,
              level          : OpenAjax.a11y.WCAG20_LEVEL.AAA,
              title          : '3.3.5 Help',
              description    : 'Context-sensitive help is available.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#minimize-error-context-help',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-minimize-error-context-help',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/minimize-error-context-help.html',
              references     : []
            },
            //
            // Success Criteria 3.3.6 Error Prevention (All)
            //
            '3.3.6': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_3_3_6,
              level          : OpenAjax.a11y.WCAG20_LEVEL.AAA,
              title          : '3.3.6 Error Prevention (All)',
              description    : 'For Web pages that require the user to submit information, at least one of the following is true',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#minimize-error-reversible-all',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-minimize-error-reversible-all',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/minimize-error-reversible-all.html',
              references     : []
            }
          }
        }
      }
    },  
    //
    // Principle 4: Robust
    //
    '4' : {
      id          : OpenAjax.a11y.WCAG20_PRINCIPLE.P_4,
      title       : 'Principle 4: Robust',
      description : 'Content must be robust enough that it can be interpreted reliably by a wide variety of user agents, including assistive technologies.', 
      url_spec    : 'http://www.w3.org/TR/WCAG20/#robust',
      guidelines : {
        //
        // Guideline 4.1 Compatible
        //
        '4.1' : {
          id          : OpenAjax.a11y.WCAG20_GUIDELINE.G_4_1,  
          title       : 'Guideline 4.1 Compatible',
          description : 'Maximize compatibility with current and future user agents, including assistive technologies.', 
          url_spec    : 'http://www.w3.org/TR/WCAG20/#ensure-compat',
          success_criteria : {
            //
            // Success Criteria 4.2.1 Parsing Content
            //
            '4.1.1': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_4_1_1,
              level          : OpenAjax.a11y.WCAG20_LEVEL.A,
              title          : '4.1.1 Parsing Content',
              description    : 'In content implemented using markup languages, elements have complete start and end tags, elements are nested according to their specifications, elements do not contain duplicate attributes, and any IDs are unique, except where the specifications allow these features.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#ensure-compat-parses',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-ensure-compat-parses',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/ensure-compat-parses.html',
              references     : []
            },
            //
            // Success Criteria 4.2.2 Name, Role, Value
            //
            '4.1.2': {
              id             : OpenAjax.a11y.WCAG20_SUCCESS_CRITERION.SC_4_1_2,
              level          : OpenAjax.a11y.WCAG20_LEVEL.A,
              title          : '4.1.2 Name, Role, Value',
              description    : 'For all user interface components (including but not limited to: form elements, links and components generated by scripts), the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to user agents, including assistive technologies.',
              url_spec       : 'http://www.w3.org/TR/WCAG20/#ensure-compat-rsv',
              url_meet       : 'http://www.w3.org/WAI/WCAG20/quickref/#qr-ensure-compat-rsv',
              url_understand : 'http://www.w3.org/TR/UNDERSTANDING-WCAG20/ensure-compat-rsv.html',
              references     : []
            }
          }
        }  
      }
    }
  }
});
